Compare commits
22 Commits
master
...
2.0-compat
| Author | SHA1 | Date |
|---|---|---|
|
|
706d5d139d | |
|
|
39f6b586cc | |
|
|
06474aacb0 | |
|
|
21cd001f51 | |
|
|
95ef42d48c | |
|
|
94acdcd527 | |
|
|
0c2c9965f0 | |
|
|
7f966ae69c | |
|
|
34b37cc391 | |
|
|
1443ffe11b | |
|
|
65d28ab294 | |
|
|
6df9f76669 | |
|
|
f03729c9ca | |
|
|
980226ccb7 | |
|
|
f58453da01 | |
|
|
28f6d7eae2 | |
|
|
94c52e3975 | |
|
|
eff17b8317 | |
|
|
e99e1b97e9 | |
|
|
2b2af218a5 | |
|
|
57f0700618 | |
|
|
28f720ea32 |
59
README.md
59
README.md
|
|
@ -8,55 +8,21 @@ It provides:
|
||||||
- An UIImageView category adding web image and cache management to the Cocoa Touch framework
|
- An UIImageView category adding web image and cache management to the Cocoa Touch framework
|
||||||
- An asynchronous image downloader
|
- An asynchronous image downloader
|
||||||
- An asynchronous memory + disk image caching with automatic cache expiration handling
|
- An asynchronous memory + disk image caching with automatic cache expiration handling
|
||||||
|
- A background image decompression
|
||||||
- A guarantee that the same URL won't be downloaded several times
|
- A guarantee that the same URL won't be downloaded several times
|
||||||
- A guarantee that bogus URLs won't be retried again and again
|
- A guarantee that bogus URLs won't be retried again and again
|
||||||
|
- A guarantee that main thread will never be blocked
|
||||||
- Performances!
|
- Performances!
|
||||||
|
|
||||||
Motivation
|
[How is SDWebImage better than X?](https://github.com/rs/SDWebImage/wiki/How-is-SDWebImage-better-than-X%3F)
|
||||||
|
|
||||||
|
Who Use It
|
||||||
----------
|
----------
|
||||||
|
|
||||||
As a dummy Objective-C developer working on my first iPhone application for my company
|
Find out [who use SDWebImage](https://github.com/rs/SDWebImage/wiki/Who-Use-SDWebImage) and add your app to the list.
|
||||||
([Dailymotion][]), I've been very frustrated by the lack of support in the Cocoa Touch framework for
|
|
||||||
UITableView with remote images. After some Googling, I found lot of forums and blogs coming up with
|
|
||||||
their solution, most of the time based on asynchronous usage with NSURLConnection, but none provided
|
|
||||||
a simple library doing the work of async image grabbing + caching for you.
|
|
||||||
|
|
||||||
Actually there is one in the famous [Three20][] framework by [Joe Hewitt][], but it's a massive
|
How To Use
|
||||||
and undocumented piece of code. You can't import just the the libraries you want without taking the
|
----------
|
||||||
whole framework (damn #import "TTGlobal.h"). Anyway, the [Three20][] implementation is based on
|
|
||||||
NSURLConnection, and I soon discovered this solution wasn't ideal. Keep reading to find out why.
|
|
||||||
|
|
||||||
As a hurried beginner in iPhone development, I couldn't attempt to implement my own async image
|
|
||||||
grabber with caching support as my first steps in this new world. Thus, I asked for help from my good
|
|
||||||
friend Sebastien Flory ([Fraggle][]), who was working on his great iPhone game ([Urban Rivals][], a
|
|
||||||
future app-store hit) for almost a year. He spent quite an amount of time implementing the very
|
|
||||||
same solution for his needs, and was kind enough to give me his implementation for my own use. This
|
|
||||||
worked quite well and allowed me to concentrate on other parts of my application. But when I started
|
|
||||||
to compare my application with its direct competitor - the built-in Youtube application - I was very
|
|
||||||
unhappy with the loading speed of the images. After some network sniffing, I found that every HTTP
|
|
||||||
requests for my images was 10 times slower than Youtube's... On my own network, Youtube was 10
|
|
||||||
time faster than my own servers... WTF??
|
|
||||||
|
|
||||||
In fact, my servers were fine but a lot of latency was added to the requests, certainly because my
|
|
||||||
application wasn't responsive enough to handle the requests at full speed. Right then, I
|
|
||||||
understood something important, asynchronous NSURLConnections are tied to the main runloop in the
|
|
||||||
NSEventTrackingRunLoopMode. As explained in the documentation, this runloop mode is affected by
|
|
||||||
UI events:
|
|
||||||
|
|
||||||
> Cocoa uses this mode to restrict incoming events during mouse-dragging loops and other sorts of
|
|
||||||
> user interface tracking loops.
|
|
||||||
|
|
||||||
A simple test to recognize an application using NSURLConnection in its default mode to load
|
|
||||||
remote images is to scroll the UITableView with your finger to disclose an unloaded image, and to
|
|
||||||
keep your finger pressed on the screen. If the image doesn't load until you release you finger,
|
|
||||||
you've got one (try with the Facebook app for instance). It took me quite some time to understand
|
|
||||||
the reason for this lagging issue. Actually I first used NSOperation to workaround this issue.
|
|
||||||
|
|
||||||
This technique combined with an image cache instantly gave a lot of responsiveness to my app.
|
|
||||||
I thought this library could benefit other Cocoa Touch applications so I open-sourced it.
|
|
||||||
|
|
||||||
How To Use It
|
|
||||||
-------------
|
|
||||||
|
|
||||||
API documentation is available at [http://hackemist.com/SDWebImage/doc/](http://hackemist.com/SDWebImage/doc/)
|
API documentation is available at [http://hackemist.com/SDWebImage/doc/](http://hackemist.com/SDWebImage/doc/)
|
||||||
|
|
||||||
|
|
@ -253,9 +219,6 @@ Future Enhancements
|
||||||
|
|
||||||
- LRU memory cache cleanup instead of reset on memory warning
|
- LRU memory cache cleanup instead of reset on memory warning
|
||||||
|
|
||||||
[Dailymotion]: http://www.dailymotion.com
|
## Licenses
|
||||||
[Fraggle]: http://fraggle.squarespace.com
|
|
||||||
[Urban Rivals]: http://fraggle.squarespace.com/blog/2009/9/15/almost-done-here-is-urban-rivals-iphone-trailer.html
|
All source code is licensed under the [MIT License](https://raw.github.com/rs/SDWebImage/master/LICENSE).
|
||||||
[Three20]: http://groups.google.com/group/three20
|
|
||||||
[Joe Hewitt]: http://www.joehewitt.com
|
|
||||||
[tutorial]: http://blog.carbonfive.com/2011/04/04/using-open-source-static-libraries-in-xcode-4
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,10 @@
|
||||||
|
|
||||||
- (void)cancelCurrentImageLoad
|
- (void)cancelCurrentImageLoad
|
||||||
{
|
{
|
||||||
[[SDWebImageManager sharedManager] cancelForDelegate:self];
|
@synchronized(self)
|
||||||
|
{
|
||||||
|
[[SDWebImageManager sharedManager] cancelForDelegate:self];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url
|
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ NS_INLINE UIImage *SDScaledImageForPath(NSString *path, NSObject *imageOrData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:UIImageOrientationUp];
|
UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation];
|
||||||
SDWISafeRelease(image)
|
SDWISafeRelease(image)
|
||||||
image = scaledImage;
|
image = scaledImage;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,37 +96,18 @@ static SDWebImageDecoder *sharedInstance;
|
||||||
|
|
||||||
+ (UIImage *)decodedImageWithImage:(UIImage *)image
|
+ (UIImage *)decodedImageWithImage:(UIImage *)image
|
||||||
{
|
{
|
||||||
CGImageRef imageRef = image.CGImage;
|
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(image.CGImage);
|
||||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
||||||
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);
|
|
||||||
|
|
||||||
BOOL imageHasAlphaInfo = (alphaInfo != kCGImageAlphaNone &&
|
BOOL imageHasAlphaInfo = (alphaInfo != kCGImageAlphaNone &&
|
||||||
alphaInfo != kCGImageAlphaNoneSkipFirst &&
|
alphaInfo != kCGImageAlphaNoneSkipFirst &&
|
||||||
alphaInfo != kCGImageAlphaNoneSkipLast);
|
alphaInfo != kCGImageAlphaNoneSkipLast);
|
||||||
|
|
||||||
int bytesPerPixel = alphaInfo != kCGImageAlphaNone ? 4 : 3;
|
UIGraphicsBeginImageContextWithOptions(image.size, !imageHasAlphaInfo, 0);
|
||||||
CGBitmapInfo bitmapInfo = imageHasAlphaInfo ? kCGImageAlphaPremultipliedLast : alphaInfo;
|
CGRect rect = (CGRect){.origin = CGPointZero, .size = image.size};
|
||||||
|
[image drawInRect:rect];
|
||||||
|
UIImage *decompressedImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
|
UIGraphicsEndImageContext();
|
||||||
|
|
||||||
CGContextRef context = CGBitmapContextCreate(NULL,
|
return decompressedImage;
|
||||||
CGImageGetWidth(imageRef),
|
|
||||||
CGImageGetHeight(imageRef),
|
|
||||||
8,
|
|
||||||
// Just always return width * bytesPerPixel will be enough
|
|
||||||
CGImageGetWidth(imageRef) * bytesPerPixel,
|
|
||||||
// System only supports RGB, set explicitly
|
|
||||||
colorSpace,
|
|
||||||
bitmapInfo);
|
|
||||||
CGColorSpaceRelease(colorSpace);
|
|
||||||
if (!context) return nil;
|
|
||||||
|
|
||||||
CGRect rect = (CGRect){CGPointZero,{CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)}};
|
|
||||||
CGContextDrawImage(context, rect, imageRef);
|
|
||||||
CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context);
|
|
||||||
CGContextRelease(context);
|
|
||||||
|
|
||||||
UIImage *decompressedImage = [[UIImage alloc] initWithCGImage:decompressedImageRef scale:image.scale orientation:image.imageOrientation];
|
|
||||||
CGImageRelease(decompressedImageRef);
|
|
||||||
return SDWIReturnAutoreleased(decompressedImage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNot
|
||||||
- (void)start
|
- (void)start
|
||||||
{
|
{
|
||||||
// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests
|
// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests
|
||||||
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15];
|
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:15];
|
||||||
self.connection = SDWIReturnAutoreleased([[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]);
|
self.connection = SDWIReturnAutoreleased([[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]);
|
||||||
|
|
||||||
// If not in low priority mode, ensure we aren't blocked by UI manipulations (default runloop mode for NSURLConnection is NSEventTrackingRunLoopMode)
|
// If not in low priority mode, ensure we aren't blocked by UI manipulations (default runloop mode for NSURLConnection is NSEventTrackingRunLoopMode)
|
||||||
|
|
@ -253,6 +253,12 @@ NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNot
|
||||||
self.imageData = nil;
|
self.imageData = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//prevent caching of responses in Cache.db
|
||||||
|
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
|
||||||
|
{
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark SDWebImageDecoderDelegate
|
#pragma mark SDWebImageDecoderDelegate
|
||||||
|
|
||||||
- (void)imageDecoder:(SDWebImageDecoder *)decoder didFinishDecodingImage:(UIImage *)image userInfo:(NSDictionary *)aUserInfo
|
- (void)imageDecoder:(SDWebImageDecoder *)decoder didFinishDecodingImage:(UIImage *)image userInfo:(NSDictionary *)aUserInfo
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,19 @@ static SDWebImageManager *instance;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
- (void)removeObjectsForDelegate:(id<SDWebImageManagerDelegate>)delegate
|
||||||
|
{
|
||||||
|
// Delegates notified, remove downloader and delegate
|
||||||
|
// The delegate callbacks above may have modified the arrays, hence we search for the correct index
|
||||||
|
NSUInteger idx = [downloadDelegates indexOfObjectIdenticalTo:delegate];
|
||||||
|
if (idx != NSNotFound)
|
||||||
|
{
|
||||||
|
[downloaders removeObjectAtIndex:idx];
|
||||||
|
[downloadInfo removeObjectAtIndex:idx];
|
||||||
|
[downloadDelegates removeObjectAtIndex:idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void)cancelForDelegate:(id<SDWebImageManagerDelegate>)delegate
|
- (void)cancelForDelegate:(id<SDWebImageManagerDelegate>)delegate
|
||||||
{
|
{
|
||||||
NSUInteger idx;
|
NSUInteger idx;
|
||||||
|
|
@ -258,7 +271,7 @@ static SDWebImageManager *instance;
|
||||||
}
|
}
|
||||||
if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:)])
|
if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:)])
|
||||||
{
|
{
|
||||||
objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:), self, image, url);
|
((void ( *)(id, SEL, id, id, id))objc_msgSend)(delegate, @selector(webImageManager:didFinishWithImage:forURL:), self, image, url);
|
||||||
}
|
}
|
||||||
if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:userInfo:)])
|
if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:userInfo:)])
|
||||||
{
|
{
|
||||||
|
|
@ -267,7 +280,7 @@ static SDWebImageManager *instance;
|
||||||
{
|
{
|
||||||
userInfo = nil;
|
userInfo = nil;
|
||||||
}
|
}
|
||||||
objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:userInfo:), self, image, url, userInfo);
|
((void ( *)(id, SEL, id, id, id, id))objc_msgSend)(delegate, @selector(webImageManager:didFinishWithImage:forURL:userInfo:), self, image, url, userInfo);
|
||||||
}
|
}
|
||||||
#if NS_BLOCKS_AVAILABLE
|
#if NS_BLOCKS_AVAILABLE
|
||||||
if ([info objectForKey:@"success"])
|
if ([info objectForKey:@"success"])
|
||||||
|
|
@ -277,8 +290,14 @@ static SDWebImageManager *instance;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
[cacheDelegates removeObjectAtIndex:idx];
|
// Delegates notified, remove url and delegate
|
||||||
[cacheURLs removeObjectAtIndex:idx];
|
// The delegate callbacks above may have modified the arrays, hence we search for the correct index
|
||||||
|
NSUInteger removeIdx = [self indexOfDelegate:delegate waitingForURL:url];
|
||||||
|
if (removeIdx != NSNotFound)
|
||||||
|
{
|
||||||
|
[cacheDelegates removeObjectAtIndex:removeIdx];
|
||||||
|
[cacheURLs removeObjectAtIndex:removeIdx];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)imageCache:(SDImageCache *)imageCache didNotFindImageForKey:(NSString *)key userInfo:(NSDictionary *)info
|
- (void)imageCache:(SDImageCache *)imageCache didNotFindImageForKey:(NSString *)key userInfo:(NSDictionary *)info
|
||||||
|
|
@ -326,30 +345,54 @@ static SDWebImageManager *instance;
|
||||||
|
|
||||||
- (void)imageDownloader:(SDWebImageDownloader *)downloader didUpdatePartialImage:(UIImage *)image
|
- (void)imageDownloader:(SDWebImageDownloader *)downloader didUpdatePartialImage:(UIImage *)image
|
||||||
{
|
{
|
||||||
// Notify all the downloadDelegates with this downloader
|
NSMutableArray *notifiedDelegates = [NSMutableArray arrayWithCapacity:downloaders.count];
|
||||||
for (NSInteger idx = (NSInteger)[downloaders count] - 1; idx >= 0; idx--)
|
|
||||||
|
BOOL found = YES;
|
||||||
|
while (found)
|
||||||
{
|
{
|
||||||
NSUInteger uidx = (NSUInteger)idx;
|
found = NO;
|
||||||
SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:uidx];
|
assert(downloaders.count == downloadDelegates.count);
|
||||||
if (aDownloader == downloader)
|
assert(downloaders.count == downloadInfo.count);
|
||||||
|
NSInteger count = downloaders.count;
|
||||||
|
for (NSInteger i=count-1; i>=0; --i)
|
||||||
{
|
{
|
||||||
id<SDWebImageManagerDelegate> delegate = [downloadDelegates objectAtIndex:uidx];
|
SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:i];
|
||||||
|
if (aDownloader != downloader)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
id<SDWebImageManagerDelegate> delegate = [downloadDelegates objectAtIndex:i];
|
||||||
SDWIRetain(delegate);
|
SDWIRetain(delegate);
|
||||||
SDWIAutorelease(delegate);
|
SDWIAutorelease(delegate);
|
||||||
|
|
||||||
|
if ([notifiedDelegates containsObject:delegate])
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Keep track of delegates notified
|
||||||
|
[notifiedDelegates addObject:delegate];
|
||||||
|
|
||||||
|
NSDictionary *info = [downloadInfo objectAtIndex:i];
|
||||||
|
SDWIRetain(info);
|
||||||
|
SDWIAutorelease(info);
|
||||||
|
|
||||||
if ([delegate respondsToSelector:@selector(webImageManager:didProgressWithPartialImage:forURL:)])
|
if ([delegate respondsToSelector:@selector(webImageManager:didProgressWithPartialImage:forURL:)])
|
||||||
{
|
{
|
||||||
objc_msgSend(delegate, @selector(webImageManager:didProgressWithPartialImage:forURL:), self, image, downloader.url);
|
((void ( *)(id, SEL, id, id, id))objc_msgSend)(delegate, @selector(webImageManager:didProgressWithPartialImage:forURL:), self, image, downloader.url);
|
||||||
}
|
}
|
||||||
if ([delegate respondsToSelector:@selector(webImageManager:didProgressWithPartialImage:forURL:userInfo:)])
|
if ([delegate respondsToSelector:@selector(webImageManager:didProgressWithPartialImage:forURL:userInfo:)])
|
||||||
{
|
{
|
||||||
NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"];
|
NSDictionary *userInfo = [info objectForKey:@"userInfo"];
|
||||||
if ([userInfo isKindOfClass:NSNull.class])
|
if ([userInfo isKindOfClass:NSNull.class])
|
||||||
{
|
{
|
||||||
userInfo = nil;
|
userInfo = nil;
|
||||||
}
|
}
|
||||||
objc_msgSend(delegate, @selector(webImageManager:didProgressWithPartialImage:forURL:userInfo:), self, image, downloader.url, userInfo);
|
((void ( *)(id, SEL, id, id, id, id))objc_msgSend)(delegate, @selector(webImageManager:didProgressWithPartialImage:forURL:userInfo:), self, image, downloader.url, userInfo);
|
||||||
}
|
}
|
||||||
|
// Delegate notified. Break out and restart loop
|
||||||
|
found = YES;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -358,17 +401,26 @@ static SDWebImageManager *instance;
|
||||||
{
|
{
|
||||||
SDWIRetain(downloader);
|
SDWIRetain(downloader);
|
||||||
SDWebImageOptions options = [[downloader.userInfo objectForKey:@"options"] intValue];
|
SDWebImageOptions options = [[downloader.userInfo objectForKey:@"options"] intValue];
|
||||||
|
BOOL found = YES;
|
||||||
// Notify all the downloadDelegates with this downloader
|
while (found)
|
||||||
for (NSInteger idx = (NSInteger)[downloaders count] - 1; idx >= 0; idx--)
|
|
||||||
{
|
{
|
||||||
NSUInteger uidx = (NSUInteger)idx;
|
found = NO;
|
||||||
SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:uidx];
|
assert(downloaders.count == downloadDelegates.count);
|
||||||
if (aDownloader == downloader)
|
assert(downloaders.count == downloadInfo.count);
|
||||||
|
NSInteger count = downloaders.count;
|
||||||
|
for (NSInteger i=count-1; i>=0; --i)
|
||||||
{
|
{
|
||||||
id<SDWebImageManagerDelegate> delegate = [downloadDelegates objectAtIndex:uidx];
|
SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:i];
|
||||||
|
if (aDownloader != downloader)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
id<SDWebImageManagerDelegate> delegate = [downloadDelegates objectAtIndex:i];
|
||||||
SDWIRetain(delegate);
|
SDWIRetain(delegate);
|
||||||
SDWIAutorelease(delegate);
|
SDWIAutorelease(delegate);
|
||||||
|
NSDictionary *info = [downloadInfo objectAtIndex:i];
|
||||||
|
SDWIRetain(info);
|
||||||
|
SDWIAutorelease(info);
|
||||||
|
|
||||||
if (image)
|
if (image)
|
||||||
{
|
{
|
||||||
|
|
@ -378,21 +430,21 @@ static SDWebImageManager *instance;
|
||||||
}
|
}
|
||||||
if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:)])
|
if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:)])
|
||||||
{
|
{
|
||||||
objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:), self, image, downloader.url);
|
((void ( *)(id, SEL, id, id, id))objc_msgSend)(delegate, @selector(webImageManager:didFinishWithImage:forURL:), self, image, downloader.url);
|
||||||
}
|
}
|
||||||
if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:userInfo:)])
|
if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:userInfo:)])
|
||||||
{
|
{
|
||||||
NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"];
|
NSDictionary *userInfo = [info objectForKey:@"userInfo"];
|
||||||
if ([userInfo isKindOfClass:NSNull.class])
|
if ([userInfo isKindOfClass:NSNull.class])
|
||||||
{
|
{
|
||||||
userInfo = nil;
|
userInfo = nil;
|
||||||
}
|
}
|
||||||
objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:userInfo:), self, image, downloader.url, userInfo);
|
((void ( *)(id, SEL, id, id, id, id))objc_msgSend)(delegate, @selector(webImageManager:didFinishWithImage:forURL:userInfo:), self, image, downloader.url, userInfo);
|
||||||
}
|
}
|
||||||
#if NS_BLOCKS_AVAILABLE
|
#if NS_BLOCKS_AVAILABLE
|
||||||
if ([[downloadInfo objectAtIndex:uidx] objectForKey:@"success"])
|
if ([info objectForKey:@"success"])
|
||||||
{
|
{
|
||||||
SDWebImageSuccessBlock success = [[downloadInfo objectAtIndex:uidx] objectForKey:@"success"];
|
SDWebImageSuccessBlock success = [info objectForKey:@"success"];
|
||||||
success(image, NO);
|
success(image, NO);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -405,29 +457,29 @@ static SDWebImageManager *instance;
|
||||||
}
|
}
|
||||||
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:)])
|
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:)])
|
||||||
{
|
{
|
||||||
objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:), self, nil, downloader.url);
|
((void ( *)(id, SEL, id, id, id))objc_msgSend)(delegate, @selector(webImageManager:didFailWithError:forURL:), self, nil, downloader.url);
|
||||||
}
|
}
|
||||||
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:userInfo:)])
|
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:userInfo:)])
|
||||||
{
|
{
|
||||||
NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"];
|
NSDictionary *userInfo = [info objectForKey:@"userInfo"];
|
||||||
if ([userInfo isKindOfClass:NSNull.class])
|
if ([userInfo isKindOfClass:NSNull.class])
|
||||||
{
|
{
|
||||||
userInfo = nil;
|
userInfo = nil;
|
||||||
}
|
}
|
||||||
objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:userInfo:), self, nil, downloader.url, userInfo);
|
((void ( *)(id, SEL, id, id, id, id))objc_msgSend)(delegate, @selector(webImageManager:didFailWithError:forURL:userInfo:), self, nil, downloader.url, userInfo);
|
||||||
}
|
}
|
||||||
#if NS_BLOCKS_AVAILABLE
|
#if NS_BLOCKS_AVAILABLE
|
||||||
if ([[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"])
|
if ([info objectForKey:@"failure"])
|
||||||
{
|
{
|
||||||
SDWebImageFailureBlock failure = [[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"];
|
SDWebImageFailureBlock failure = [info objectForKey:@"failure"];
|
||||||
failure(nil);
|
failure(nil);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
// Downloader found. Break out and restart for loop
|
||||||
[downloaders removeObjectAtIndex:uidx];
|
[self removeObjectsForDelegate:delegate];
|
||||||
[downloadInfo removeObjectAtIndex:uidx];
|
found = YES;
|
||||||
[downloadDelegates removeObjectAtIndex:uidx];
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -457,15 +509,26 @@ static SDWebImageManager *instance;
|
||||||
SDWIRetain(downloader);
|
SDWIRetain(downloader);
|
||||||
|
|
||||||
// Notify all the downloadDelegates with this downloader
|
// Notify all the downloadDelegates with this downloader
|
||||||
for (NSInteger idx = (NSInteger)[downloaders count] - 1; idx >= 0; idx--)
|
BOOL found = YES;
|
||||||
|
while (found)
|
||||||
{
|
{
|
||||||
NSUInteger uidx = (NSUInteger)idx;
|
found = NO;
|
||||||
SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:uidx];
|
assert(downloaders.count == downloadDelegates.count);
|
||||||
if (aDownloader == downloader)
|
assert(downloaders.count == downloadInfo.count);
|
||||||
|
NSInteger count = downloaders.count;
|
||||||
|
for (NSInteger i=count-1 ; i>=0; --i)
|
||||||
{
|
{
|
||||||
id<SDWebImageManagerDelegate> delegate = [downloadDelegates objectAtIndex:uidx];
|
SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:i];
|
||||||
|
if (aDownloader != downloader)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
id<SDWebImageManagerDelegate> delegate = [downloadDelegates objectAtIndex:i];
|
||||||
SDWIRetain(delegate);
|
SDWIRetain(delegate);
|
||||||
SDWIAutorelease(delegate);
|
SDWIAutorelease(delegate);
|
||||||
|
NSDictionary *info = [downloadInfo objectAtIndex:i];
|
||||||
|
SDWIRetain(info);
|
||||||
|
SDWIAutorelease(info);
|
||||||
|
|
||||||
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:)])
|
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:)])
|
||||||
{
|
{
|
||||||
|
|
@ -473,28 +536,28 @@ static SDWebImageManager *instance;
|
||||||
}
|
}
|
||||||
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:)])
|
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:)])
|
||||||
{
|
{
|
||||||
objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:), self, error, downloader.url);
|
((void ( *)(id, SEL, id, id, id))objc_msgSend)(delegate, @selector(webImageManager:didFailWithError:forURL:), self, error, downloader.url);
|
||||||
}
|
}
|
||||||
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:userInfo:)])
|
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:userInfo:)])
|
||||||
{
|
{
|
||||||
NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"];
|
NSDictionary *userInfo = [info objectForKey:@"userInfo"];
|
||||||
if ([userInfo isKindOfClass:NSNull.class])
|
if ([userInfo isKindOfClass:NSNull.class])
|
||||||
{
|
{
|
||||||
userInfo = nil;
|
userInfo = nil;
|
||||||
}
|
}
|
||||||
objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:userInfo:), self, error, downloader.url, userInfo);
|
((void ( *)(id, SEL, id, id, id, id))objc_msgSend)(delegate, @selector(webImageManager:didFailWithError:forURL:userInfo:), self, error, downloader.url, userInfo);
|
||||||
}
|
}
|
||||||
#if NS_BLOCKS_AVAILABLE
|
#if NS_BLOCKS_AVAILABLE
|
||||||
if ([[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"])
|
if ([info objectForKey:@"failure"])
|
||||||
{
|
{
|
||||||
SDWebImageFailureBlock failure = [[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"];
|
SDWebImageFailureBlock failure = [info objectForKey:@"failure"];
|
||||||
failure(error);
|
failure(error);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
// Downloader found. Break out and restart for loop
|
||||||
[downloaders removeObjectAtIndex:uidx];
|
[self removeObjectsForDelegate:delegate];
|
||||||
[downloadInfo removeObjectAtIndex:uidx];
|
found = YES;
|
||||||
[downloadDelegates removeObjectAtIndex:uidx];
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ static SDWebImagePrefetcher *instance;
|
||||||
- (void)reportStatus
|
- (void)reportStatus
|
||||||
{
|
{
|
||||||
NSUInteger total = [self.prefetchURLs count];
|
NSUInteger total = [self.prefetchURLs count];
|
||||||
NSLog(@"Finished prefetching (%d successful, %d skipped, timeElasped %.2f)", total - _skippedCount, _skippedCount, CFAbsoluteTimeGetCurrent() - _startedTime);
|
NSLog(@"Finished prefetching (%lu successful, %lu skipped, timeElasped %.2f)", (unsigned long)(total - _skippedCount), (unsigned long)_skippedCount, CFAbsoluteTimeGetCurrent() - _startedTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)prefetchURLs:(NSArray *)urls
|
- (void)prefetchURLs:(NSArray *)urls
|
||||||
|
|
@ -75,7 +75,7 @@ static SDWebImagePrefetcher *instance;
|
||||||
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image
|
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image
|
||||||
{
|
{
|
||||||
_finishedCount++;
|
_finishedCount++;
|
||||||
NSLog(@"Prefetched %d out of %d", _finishedCount, [self.prefetchURLs count]);
|
NSLog(@"Prefetched %lu out of %lu", (unsigned long)_finishedCount, (unsigned long)[self.prefetchURLs count]);
|
||||||
|
|
||||||
if ([self.prefetchURLs count] > _requestedCount)
|
if ([self.prefetchURLs count] > _requestedCount)
|
||||||
{
|
{
|
||||||
|
|
@ -90,7 +90,7 @@ static SDWebImagePrefetcher *instance;
|
||||||
- (void)webImageManager:(SDWebImageManager *)imageManager didFailWithError:(NSError *)error
|
- (void)webImageManager:(SDWebImageManager *)imageManager didFailWithError:(NSError *)error
|
||||||
{
|
{
|
||||||
_finishedCount++;
|
_finishedCount++;
|
||||||
NSLog(@"Prefetched %d out of %d (Failed)", _finishedCount, [self.prefetchURLs count]);
|
NSLog(@"Prefetched %lu out of %lu (Failed)", (unsigned long)_finishedCount, (unsigned long)[self.prefetchURLs count]);
|
||||||
|
|
||||||
// Add last failed
|
// Add last failed
|
||||||
_skippedCount++;
|
_skippedCount++;
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,10 @@
|
||||||
|
|
||||||
- (void)cancelCurrentImageLoad
|
- (void)cancelCurrentImageLoad
|
||||||
{
|
{
|
||||||
[[SDWebImageManager sharedManager] cancelForDelegate:self];
|
@synchronized(self)
|
||||||
|
{
|
||||||
|
[[SDWebImageManager sharedManager] cancelForDelegate:self];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url userInfo:(NSDictionary *)info
|
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url userInfo:(NSDictionary *)info
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,10 @@
|
||||||
|
|
||||||
- (void)cancelCurrentImageLoad
|
- (void)cancelCurrentImageLoad
|
||||||
{
|
{
|
||||||
[[SDWebImageManager sharedManager] cancelForDelegate:self];
|
@synchronized(self)
|
||||||
|
{
|
||||||
|
[[SDWebImageManager sharedManager] cancelForDelegate:self];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url
|
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue