Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added an NSOperationQueue for remote downloads. Made image loads from…

… disk asynchronous. Added a category to NSOperationQueue to make it behave like a stack, which is more likely what people want.
  • Loading branch information...
commit b1f1a09f8007de3cd02daff9d49e76307aa77cbc 1 parent 5621342
@cbrauchli authored
View
1  JMImageCache.h
@@ -25,6 +25,7 @@
- (void) imageForURL:(NSURL *)url key:(NSString *)key completionBlock:(void (^)(UIImage *image))completion;
- (void) imageForURL:(NSURL *)url completionBlock:(void (^)(UIImage *image))completion;
+- (id) objectForURL:(NSURL *)url;
- (UIImage *) cachedImageForKey:(NSString *)key;
- (UIImage *) cachedImageForURL:(NSURL *)url;
View
71 JMImageCache.m
@@ -7,6 +7,7 @@
//
#import "JMImageCache.h"
+#import "NSOperationQueue+LIFO.h"
static NSString *_JMImageCacheDirectory;
static dispatch_once_t onceToken;
@@ -31,7 +32,9 @@
@interface JMImageCache ()
@property (strong, nonatomic) NSOperationQueue *diskOperationQueue;
+@property (strong, nonatomic) NSOperationQueue *downloadOperationQueue;
+- (void) _asyncGetImageFromDiskOrRemoteSourceForURL:(NSURL *)url key:(NSString *)key completionBlock:(void (^)(UIImage *image))completion;
- (void) _downloadAndWriteImageForURL:(NSURL *)url key:(NSString *)key completionBlock:(void (^)(UIImage *image))completion;
@end
@@ -39,6 +42,7 @@ - (void) _downloadAndWriteImageForURL:(NSURL *)url key:(NSString *)key completio
@implementation JMImageCache
@synthesize diskOperationQueue = _diskOperationQueue;
+@synthesize downloadOperationQueue = _downloadOperationQueue;
+ (JMImageCache *) sharedCache {
if(!_sharedCache) {
@@ -53,6 +57,9 @@ - (id) init {
if(!self) return nil;
self.diskOperationQueue = [[NSOperationQueue alloc] init];
+ self.diskOperationQueue.maxConcurrentOperationCount = 1;
+ self.downloadOperationQueue = [[NSOperationQueue alloc] init];
+ self.downloadOperationQueue.maxConcurrentOperationCount = 10;
[[NSFileManager defaultManager] createDirectoryAtPath:JMImageCacheDirectory()
withIntermediateDirectories:YES
@@ -61,32 +68,63 @@ - (id) init {
return self;
}
+- (void) _asyncGetImageFromDiskOrRemoteSourceForURL:(NSURL *)url key:(NSString *)key completionBlock:(void (^)(UIImage *image))completion {
+ if (!key && !url) return;
+
+ if (!key) {
+ key = keyForURL(url);
+ }
+
+ NSInvocationOperation *diskReadOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(imageFromDiskForKey:) object:key];
+ [diskReadOperation setCompletionBlock:^{
+ UIImage *i = diskReadOperation.result;
+
+ if (i) {
+ [self setImage:i forKey:key];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if(completion) completion(i);
+ });
+ } else {
+ // Have to download the image!
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self _downloadAndWriteImageForURL:url key:key completionBlock:completion];
+ });
+ }
+ }];
+ [self.diskOperationQueue addOperationAtFrontOfQueue:diskReadOperation];
+}
+
- (void) _downloadAndWriteImageForURL:(NSURL *)url key:(NSString *)key completionBlock:(void (^)(UIImage *image))completion {
if (!key && !url) return;
if (!key) {
key = keyForURL(url);
}
-
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- NSData *data = [NSData dataWithContentsOfURL:url];
+
+ NSInvocationOperation *downloadOperation = [[NSInvocationOperation alloc] initWithTarget:[NSData class] selector:@selector(dataWithContentsOfURL:) object:url];
+ [downloadOperation setCompletionBlock:^{
+ DLog(@"downaloddd!");
+ NSData *data = downloadOperation.result;
UIImage *i = [[UIImage alloc] initWithData:data];
-
+
+ [self setImage:i forKey:key];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if(completion) completion(i);
+ });
+
NSString *cachePath = cachePathForKey(key);
NSInvocation *writeInvocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(writeData:toPath:)]];
-
+
[writeInvocation setTarget:self];
[writeInvocation setSelector:@selector(writeData:toPath:)];
[writeInvocation setArgument:&data atIndex:2];
[writeInvocation setArgument:&cachePath atIndex:3];
-
+
[self performDiskWriteOperation:writeInvocation];
- [self setImage:i forKey:key];
-
- dispatch_async(dispatch_get_main_queue(), ^{
- if(completion) completion(i);
- });
- });
+ }];
+ [self.downloadOperationQueue addOperationAtFrontOfQueue:downloadOperation];
}
- (void) removeAllObjects {
@@ -132,12 +170,12 @@ - (void) removeObjectForKey:(id)key {
- (void) imageForURL:(NSURL *)url key:(NSString *)key completionBlock:(void (^)(UIImage *image))completion {
- UIImage *i = [self cachedImageForKey:key];
+ UIImage *i = [super objectForKey:key];
if(i) {
if(completion) completion(i);
} else {
- [self _downloadAndWriteImageForURL:url key:key completionBlock:completion];
+ [self _asyncGetImageFromDiskOrRemoteSourceForURL:url key:key completionBlock:completion];
}
}
@@ -145,6 +183,11 @@ - (void) imageForURL:(NSURL *)url completionBlock:(void (^)(UIImage *image))comp
[self imageForURL:url key:keyForURL(url) completionBlock:completion];
}
+- (id)objectForURL:(NSURL *)url {
+ NSString *key = keyForURL(url);
+ return [super objectForKey:key];
+}
+
- (UIImage *) cachedImageForKey:(NSString *)key {
if(!key) return nil;
View
47 NSOperationQueue+LIFO.h
@@ -0,0 +1,47 @@
+//
+// NSOperationStack.h
+//
+// Version 1.0
+//
+// Created by Nick Lockwood on 28/06/2012.
+// Copyright (c) 2012 Charcoal Design
+//
+// Distributed under the permissive zlib License
+// Get the latest version from here:
+//
+// https://github.com/nicklockwood/NSOperationStack
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#import <Foundation/Foundation.h>
+
+
+@interface NSOperationQueue (LIFO)
+
+- (void)addOperationAtFrontOfQueue:(NSOperation *)op;
+- (void)addOperationsAtFrontOfQueue:(NSArray *)ops waitUntilFinished:(BOOL)wait;
+- (void)addOperationAtFrontOfQueueWithBlock:(void (^)(void))block;
+
+@end
+
+
+@interface NSOperationStack : NSOperationQueue
+
+@end
View
107 NSOperationQueue+LIFO.m
@@ -0,0 +1,107 @@
+//
+// NSOperationStack.m
+//
+// Version 1.0
+//
+// Created by Nick Lockwood on 28/06/2012.
+// Copyright (c) 2012 Charcoal Design
+//
+// Distributed under the permissive zlib License
+// Get the latest version from here:
+//
+// https://github.com/nicklockwood/NSOperationStack
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#import "NSOperationQueue+LIFO.h"
+
+
+@implementation NSOperationQueue (LIFO)
+
+- (void)setLIFODependendenciesForOperation:(NSOperation *)op
+{
+ @synchronized(self)
+ {
+ //suspend queue
+ BOOL wasSuspended = [self isSuspended];
+ [self setSuspended:YES];
+
+ //make op a dependency of all queued ops
+ NSInteger index = [[self operations] count] - [self maxConcurrentOperationCount];
+ if (index >= 0)
+ {
+ NSOperation *operation = [[self operations] objectAtIndex:index];
+ if (![operation isExecuting])
+ {
+ [operation addDependency:op];
+ }
+ }
+
+ //resume queue
+ [self setSuspended:wasSuspended];
+ }
+}
+
+- (void)addOperationAtFrontOfQueue:(NSOperation *)op
+{
+ [self setLIFODependendenciesForOperation:op];
+ [self addOperation:op];
+}
+
+- (void)addOperationsAtFrontOfQueue:(NSArray *)ops waitUntilFinished:(BOOL)wait
+{
+ for (NSOperation *op in ops)
+ {
+ [self setLIFODependendenciesForOperation:op];
+ }
+ [self addOperations:ops waitUntilFinished:wait];
+}
+
+- (void)addOperationAtFrontOfQueueWithBlock:(void (^)(void))block
+{
+ [self addOperationAtFrontOfQueue:[NSBlockOperation blockOperationWithBlock:block]];
+}
+
+@end
+
+
+@implementation NSOperationStack
+
+- (void)addOperation:(NSOperation *)op
+{
+ [self setLIFODependendenciesForOperation:op];
+ [super addOperation:op];
+}
+
+- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait
+{
+ for (NSOperation *op in ops)
+ {
+ [self setLIFODependendenciesForOperation:op];
+ }
+ [super addOperations:ops waitUntilFinished:wait];
+}
+
+- (void)addOperationWithBlock:(void (^)(void))block
+{
+ [self addOperation:[NSBlockOperation blockOperationWithBlock:block]];
+}
+
+@end
View
4 UIImageView+JMImageCache.m
@@ -49,9 +49,9 @@ - (void) setImageWithURL:(NSURL *)url key:(NSString*)key placeholder:(UIImage *)
UIImage *i;
if (key) {
- i = [[JMImageCache sharedCache] cachedImageForKey:key];
+ i = [[JMImageCache sharedCache] objectForKey:key];
} else {
- i = [[JMImageCache sharedCache] cachedImageForURL:url];
+ i = [[JMImageCache sharedCache] objectForURL:url];
}
if(i) {
Please sign in to comment.
Something went wrong with that request. Please try again.