Permalink
Browse files

Added in (optional) support for using blocks instead of notifications…

…. If you use blocks, you can use the new 'styler' feature to manipulate the image ahead of time and cache the manipulated image, instead of manipulating it everytime you draw it. Also added a method to clear cache.
  • Loading branch information...
1 parent 3a28f93 commit c8fbdaa61d6ef7114219e3e7c602abf1072cc2dc @shnhrrsn shnhrrsn committed Apr 30, 2011
@@ -52,6 +52,10 @@
@property(nonatomic,assign) NSTimeInterval timeoutInterval; // Default is 30 seconds
+#if __EGOIL_USE_BLOCKS
+@property(nonatomic,readonly) NSMutableDictionary* handlers;
+#endif
+
@end
@protocol EGOImageLoadConnectionDelegate<NSObject>
@@ -30,12 +30,20 @@
@implementation EGOImageLoadConnection
@synthesize imageURL=_imageURL, response=_response, delegate=_delegate, timeoutInterval=_timeoutInterval;
+#if __EGOIL_USE_BLOCKS
+@synthesize handlers;
+#endif
+
- (id)initWithImageURL:(NSURL*)aURL delegate:(id)delegate {
if((self = [super init])) {
_imageURL = [aURL retain];
self.delegate = delegate;
_responseData = [[NSMutableData alloc] init];
self.timeoutInterval = 30;
+
+ #if __EGOIL_USE_BLOCKS
+ handlers = [[NSMutableDictionary alloc] init];
+ #endif
}
return self;
@@ -88,6 +96,11 @@ - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)err
- (void)dealloc {
self.response = nil;
self.delegate = nil;
+
+ #if __EGOIL_USE_BLOCKS
+ [handlers release], handlers = nil;
+ #endif
+
[_connection release];
[_imageURL release];
[super dealloc];
@@ -26,6 +26,14 @@
#import <Foundation/Foundation.h>
+#ifndef __EGOIL_USE_BLOCKS
+#define __EGOIL_USE_BLOCKS 0
+#endif
+
+#ifndef __EGOIL_USE_NOTIF
+#define __EGOIL_USE_NOTIF 1
+#endif
+
@protocol EGOImageLoaderObserver;
@interface EGOImageLoader : NSObject/*<NSURLConnectionDelegate>*/ {
@private
@@ -38,14 +46,25 @@
+ (EGOImageLoader*)sharedImageLoader;
- (BOOL)isLoadingImageURL:(NSURL*)aURL;
+
+#if __EGOIL_USE_NOTIF
- (void)loadImageForURL:(NSURL*)aURL observer:(id<EGOImageLoaderObserver>)observer;
- (UIImage*)imageForURL:(NSURL*)aURL shouldLoadWithObserver:(id<EGOImageLoaderObserver>)observer;
-- (BOOL)hasLoadedImageURL:(NSURL*)aURL;
-
-- (void)cancelLoadForURL:(NSURL*)aURL;
- (void)removeObserver:(id<EGOImageLoaderObserver>)observer;
- (void)removeObserver:(id<EGOImageLoaderObserver>)observer forURL:(NSURL*)aURL;
+#endif
+
+#if __EGOIL_USE_BLOCKS
+- (void)loadImageForURL:(NSURL*)aURL completion:(void (^)(UIImage* image, NSURL* imageURL, NSError* error))completion;
+- (void)loadImageForURL:(NSURL*)aURL style:(NSString*)style styler:(UIImage* (^)(UIImage* image))styler completion:(void (^)(UIImage* image, NSURL* imageURL, NSError* error))completion;
+#endif
+
+- (BOOL)hasLoadedImageURL:(NSURL*)aURL;
+- (void)cancelLoadForURL:(NSURL*)aURL;
+
+- (void)clearCacheForURL:(NSURL*)aURL;
+- (void)clearCacheForURL:(NSURL*)aURL style:(NSString*)style;
@property(nonatomic,retain) NSDictionary* currentConnections;
@end
@@ -54,4 +73,4 @@
@optional
- (void)imageLoaderDidLoad:(NSNotification*)notification; // Object will be EGOImageLoader, userInfo will contain imageURL and image
- (void)imageLoaderDidFailToLoad:(NSNotification*)notification; // Object will be EGOImageLoader, userInfo will contain error
-@end
+@end
@@ -30,12 +30,28 @@
static EGOImageLoader* __imageLoader;
-inline static NSString* keyForURL(NSURL* url) {
- return [NSString stringWithFormat:@"EGOImageLoader-%u", [[url description] hash]];
+inline static NSString* keyForURL(NSURL* url, NSString* style) {
+ if(style) {
+ return [NSString stringWithFormat:@"EGOImageLoader-%u-%u", [[url description] hash], [style hash]];
+ } else {
+ return [NSString stringWithFormat:@"EGOImageLoader-%u", [[url description] hash]];
+ }
}
-#define kImageNotificationLoaded(s) [@"kEGOImageLoaderNotificationLoaded-" stringByAppendingString:keyForURL(s)]
-#define kImageNotificationLoadFailed(s) [@"kEGOImageLoaderNotificationLoadFailed-" stringByAppendingString:keyForURL(s)]
+#define kNoStyle @"EGOImageLoader-nostyle"
+#define kCompletionsKey @"completions"
+#define kStylerKey @"styler"
+
+#if __EGOIL_USE_NOTIF
+#define kImageNotificationLoaded(s) [@"kEGOImageLoaderNotificationLoaded-" stringByAppendingString:keyForURL(s, nil)]
+#define kImageNotificationLoadFailed(s) [@"kEGOImageLoaderNotificationLoadFailed-" stringByAppendingString:keyForURL(s, nil)]
+#endif
+
+@interface EGOImageLoader ()
+#if __EGOIL_USE_NOTIF
+- (void)handleCompletionsForConnection:(EGOImageLoadConnection*)connection image:(UIImage*)image error:(NSError*)error;
+#endif
+@end
@implementation EGOImageLoader
@synthesize currentConnections=_currentConnections;
@@ -76,6 +92,14 @@ - (void)cleanUpConnection:(EGOImageLoadConnection*)connection {
[connectionsLock unlock];
}
+- (void)clearCacheForURL:(NSURL*)aURL {
+ [self clearCacheForURL:aURL style:nil];
+}
+
+- (void)clearCacheForURL:(NSURL*)aURL style:(NSString*)style {
+ [[EGOCache currentCache] removeCacheForKey:keyForURL(aURL, style)];
+}
+
- (BOOL)isLoadingImageURL:(NSURL*)aURL {
return [self loadingConnectionForURL:aURL] ? YES : NO;
}
@@ -87,6 +111,26 @@ - (void)cancelLoadForURL:(NSURL*)aURL {
[self cleanUpConnection:connection];
}
+- (EGOImageLoadConnection*)loadImageForURL:(NSURL*)aURL {
+ EGOImageLoadConnection* connection;
+
+ if((connection = [self loadingConnectionForURL:aURL])) {
+ return connection;
+ } else {
+ connection = [[EGOImageLoadConnection alloc] initWithImageURL:aURL delegate:self];
+
+ [connectionsLock lock];
+ [currentConnections setObject:connection forKey:aURL];
+ self.currentConnections = [[currentConnections copy] autorelease];
+ [connectionsLock unlock];
+ [connection performSelector:@selector(start) withObject:nil afterDelay:0.01];
+ [connection release];
+
+ return connection;
+ }
+}
+
+#if __EGOIL_USE_NOTIF
- (void)loadImageForURL:(NSURL*)aURL observer:(id<EGOImageLoaderObserver>)observer {
if(!aURL) return;
@@ -97,38 +141,23 @@ - (void)loadImageForURL:(NSURL*)aURL observer:(id<EGOImageLoaderObserver>)observ
if([observer respondsToSelector:@selector(imageLoaderDidFailToLoad:)]) {
[[NSNotificationCenter defaultCenter] addObserver:observer selector:@selector(imageLoaderDidFailToLoad:) name:kImageNotificationLoadFailed(aURL) object:self];
}
-
- if([self loadingConnectionForURL:aURL]) {
- return;
- }
-
- EGOImageLoadConnection* connection = [[EGOImageLoadConnection alloc] initWithImageURL:aURL delegate:self];
- [connectionsLock lock];
- [currentConnections setObject:connection forKey:aURL];
- self.currentConnections = [[currentConnections copy] autorelease];
- [connectionsLock unlock];
- [connection performSelector:@selector(start) withObject:nil afterDelay:0.01];
- [connection release];
+ [self loadImageForURL:aURL];
}
- (UIImage*)imageForURL:(NSURL*)aURL shouldLoadWithObserver:(id<EGOImageLoaderObserver>)observer {
if(!aURL) return nil;
- UIImage* anImage = [[EGOCache currentCache] imageForKey:keyForURL(aURL)];
+ UIImage* anImage = [[EGOCache currentCache] imageForKey:keyForURL(aURL,nil)];
if(anImage) {
return anImage;
} else {
- [self loadImageForURL:(NSURL*)aURL observer:observer];
+ [self loadImageForURL:aURL observer:observer];
return nil;
}
}
-- (BOOL)hasLoadedImageURL:(NSURL*)aURL {
- return [[EGOCache currentCache] hasCacheForKey:keyForURL(aURL)];
-}
-
- (void)removeObserver:(id<EGOImageLoaderObserver>)observer {
[[NSNotificationCenter defaultCenter] removeObserver:observer name:nil object:self];
}
@@ -138,6 +167,57 @@ - (void)removeObserver:(id<EGOImageLoaderObserver>)observer forURL:(NSURL*)aURL
[[NSNotificationCenter defaultCenter] removeObserver:observer name:kImageNotificationLoadFailed(aURL) object:self];
}
+#endif
+
+#if __EGOIL_USE_BLOCKS
+- (void)loadImageForURL:(NSURL*)aURL completion:(void (^)(UIImage* image, NSURL* imageURL, NSError* error))completion {
+ [self loadImageForURL:aURL style:nil styler:nil completion:completion];
+}
+
+- (void)loadImageForURL:(NSURL*)aURL style:(NSString*)style styler:(UIImage* (^)(UIImage* image))styler completion:(void (^)(UIImage* image, NSURL* imageURL, NSError* error))completion {
+ UIImage* anImage = [[EGOCache currentCache] imageForKey:keyForURL(aURL,style)];
+
+ if(anImage) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completion(anImage, aURL, nil);
+ });
+ } else if(!anImage && styler && style && (anImage = [[EGOCache currentCache] imageForKey:keyForURL(aURL,nil)])) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ UIImage* image = styler(anImage);
+ [[EGOCache currentCache] setImage:image forKey:keyForURL(aURL, style) withTimeoutInterval:604800];
+ completion(image, aURL, nil);
+ });
+ } else {
+ EGOImageLoadConnection* connection = [self loadImageForURL:aURL];
+ void (^completionCopy)(UIImage* image, NSURL* imageURL, NSError* error) = [completion copy];
+
+ NSString* handlerKey = style ? style : kNoStyle;
+ NSMutableDictionary* handler = [connection.handlers objectForKey:handlerKey];
+
+ if(!handler) {
+ handler = [[NSMutableDictionary alloc] initWithCapacity:2];
+ [connection.handlers setObject:handler forKey:handlerKey];
+
+ [handler setObject:[NSMutableArray arrayWithCapacity:1] forKey:kCompletionsKey];
+ if(styler) {
+ UIImage* (^stylerCopy)(UIImage* image) = [styler copy];
+ [handler setObject:stylerCopy forKey:kStylerKey];
+ [stylerCopy release];
+ }
+
+ [handler release];
+ }
+
+ [[handler objectForKey:kCompletionsKey] addObject:completionCopy];
+ [completionCopy release];
+ }
+}
+#endif
+
+- (BOOL)hasLoadedImageURL:(NSURL*)aURL {
+ return [[EGOCache currentCache] hasCacheForKey:keyForURL(aURL,nil)];
+}
+
#pragma mark -
#pragma mark URL Connection delegate methods
@@ -146,23 +226,38 @@ - (void)imageLoadConnectionDidFinishLoading:(EGOImageLoadConnection *)connection
if(!anImage) {
NSError* error = [NSError errorWithDomain:[connection.imageURL host] code:406 userInfo:nil];
+
+ #if __EGOIL_USE_NOTIF
NSNotification* notification = [NSNotification notificationWithName:kImageNotificationLoadFailed(connection.imageURL)
object:self
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:error,@"error",connection.imageURL,@"imageURL",nil]];
[[NSNotificationCenter defaultCenter] performSelectorOnMainThread:@selector(postNotification:) withObject:notification waitUntilDone:YES];
+ #endif
+
+ #if __EGOIL_USE_BLOCKS
+ [self handleCompletionsForConnection:connection image:nil error:error];
+ #endif
} else {
- [[EGOCache currentCache] setData:connection.responseData forKey:keyForURL(connection.imageURL) withTimeoutInterval:604800];
+ [[EGOCache currentCache] setData:connection.responseData forKey:keyForURL(connection.imageURL,nil) withTimeoutInterval:604800];
[currentConnections removeObjectForKey:connection.imageURL];
self.currentConnections = [[currentConnections copy] autorelease];
+ #if __EGOIL_USE_NOTIF
NSNotification* notification = [NSNotification notificationWithName:kImageNotificationLoaded(connection.imageURL)
object:self
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:anImage,@"image",connection.imageURL,@"imageURL",nil]];
[[NSNotificationCenter defaultCenter] performSelectorOnMainThread:@selector(postNotification:) withObject:notification waitUntilDone:YES];
+ #endif
+
+ #if __EGOIL_USE_BLOCKS
+ [self handleCompletionsForConnection:connection image:anImage error:nil];
+ #endif
}
+
+
[self cleanUpConnection:connection];
}
@@ -171,15 +266,51 @@ - (void)imageLoadConnection:(EGOImageLoadConnection *)connection didFailWithErro
[currentConnections removeObjectForKey:connection.imageURL];
self.currentConnections = [[currentConnections copy] autorelease];
+ #if __EGOIL_USE_NOTIF
NSNotification* notification = [NSNotification notificationWithName:kImageNotificationLoadFailed(connection.imageURL)
object:self
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:error,@"error",connection.imageURL,@"imageURL",nil]];
[[NSNotificationCenter defaultCenter] performSelectorOnMainThread:@selector(postNotification:) withObject:notification waitUntilDone:YES];
+ #endif
+
+ #if __EGOIL_USE_BLOCKS
+ [self handleCompletionsForConnection:connection image:nil error:error];
+ #endif
[self cleanUpConnection:connection];
}
+#if __EGOIL_USE_NOTIF
+- (void)handleCompletionsForConnection:(EGOImageLoadConnection*)connection image:(UIImage*)image error:(NSError*)error {
+ if([connection.handlers count] == 0) return;
+
+ NSURL* imageURL = connection.imageURL;
+
+ void (^callCompletions)(UIImage* anImage, NSArray* completions) = ^(UIImage* anImage, NSArray* completions) {
+ for(void (^completion)(UIImage* image, NSURL* imageURL, NSError* error) in completions) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completion(anImage, connection.imageURL, error);
+ });
+ }
+ };
+
+ for(NSString* styleKey in connection.handlers) {
+ NSDictionary* handler = [connection.handlers objectForKey:styleKey];
+ UIImage* (^styler)(UIImage* image) = [handler objectForKey:kStylerKey];
+ if(!error && image && styler) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ UIImage* anImage = styler(image);
+ [[EGOCache currentCache] setImage:anImage forKey:keyForURL(imageURL, styleKey) withTimeoutInterval:604800];
+ callCompletions(anImage, [handler objectForKey:kCompletionsKey]);
+ });
+ } else {
+ callCompletions(image, [handler objectForKey:kCompletionsKey]);
+ }
+ }
+}
+#endif
+
#pragma mark -
- (void)dealloc {

0 comments on commit c8fbdaa

Please sign in to comment.