Compare commits

...

22 Commits

Author SHA1 Message Date
Olivier Poitrey 706d5d139d Merge pull request #542 from aburgel/2.0-64bit
Fix various 64bit issues on 2.0-compat branch
2013-10-31 15:34:52 -07:00
Alex Burgel 39f6b586cc Fix crashes on 64bit devices by casting objc_msgSend calls 2013-10-31 15:02:19 -04:00
Alex Burgel 06474aacb0 Fix 64bit precision loss warnings 2013-10-31 15:01:41 -04:00
Alex Burgel 21cd001f51 Fix 64bit string formatting warnings 2013-10-31 15:00:59 -04:00
Olivier Poitrey 95ef42d48c Fix code format 2013-04-27 19:21:18 +02:00
liuzhenfu 94acdcd527 format the code to the right indentation. 2013-04-28 01:12:17 +08:00
liuzhenfu 0c2c9965f0 Fixed bugs when delegates add/cancel requests from delegate callbacks
Fixed bugs where callbacks via delegates or blocks could cause a crash since these might modify the downloaders, downloadInfo or downloadDelegates arrays if adding a new download request or canceling one from these.

https://github.com/rs/SDWebImage/pull/139
2013-04-24 14:47:37 +08:00
Olivier Poitrey 7f966ae69c Remove beta notice 2012-12-06 09:25:13 +01:00
Olivier Poitrey 34b37cc391 Merge pull request #221 from JaviSoto/master
CRASH: Fixing overrelease of UIImage object
2012-11-13 16:08:02 -08:00
Javier Soto 1443ffe11b Fixing overrelease of UIImage object 2012-11-13 16:05:50 -08:00
Olivier Poitrey 65d28ab294 Fix CGBitmapContextCreate errors (fix #204) 2012-11-14 00:45:30 +01:00
Olivier Poitrey 6df9f76669 Merge pull request #211 from herrernst/master
Prevent caching of responses in Cache.db
2012-11-05 10:54:22 -08:00
Olivier Poitrey f03729c9ca Fix wrong cache policy used (fix #211) 2012-11-05 17:53:11 +01:00
herrernst 980226ccb7 prevent caching of responses in Cache.db 2012-11-05 17:20:33 +01:00
Olivier Poitrey f58453da01 Fix typo in license URL 2012-11-05 12:20:41 +01:00
Olivier Poitrey 28f6d7eae2 Add Licenses section 2012-11-04 17:47:56 +01:00
Olivier Poitrey 94c52e3975 Add "How is SDWebImage better than X?" wiki link 2012-11-04 16:17:01 +01:00
Olivier Poitrey eff17b8317 Sync feature list with 3.0 README 2012-11-04 15:42:49 +01:00
Olivier Poitrey e99e1b97e9 Remove outdated motivation section 2012-11-04 15:40:20 +01:00
Olivier Poitrey 2b2af218a5 Add who use it section 2012-11-04 15:16:49 +01:00
Olivier Poitrey 57f0700618 Fix crash condition + invalid image orientation (fix #206) 2012-11-04 10:12:26 +01:00
Olivier Poitrey 28f720ea32 Add notice about 3.0 beta 2012-11-04 10:07:48 +01:00
9 changed files with 177 additions and 155 deletions

View File

@ -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

View File

@ -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

View File

@ -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;
} }

View File

@ -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

View File

@ -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

View File

@ -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;
} }
} }

View File

@ -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++;

View File

@ -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

View File

@ -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