From f422788c7fd94bceb3e4e61bfcf58955d45fb9e9 Mon Sep 17 00:00:00 2001 From: Jacob Jennings Date: Fri, 7 Dec 2012 11:46:23 -0800 Subject: [PATCH] Un-separate synchronous vs asynchronous in interface; call operation block directly in synchronous case to guarantee completionBlock called before returning waitUntilFinished was not reliable because it doesn't guarantee the completionBlock being called, only the operation being completed. --- MSCachedAsyncViewDrawing.h | 23 +++------- MSCachedAsyncViewDrawing.m | 93 +++++++++++++------------------------- 2 files changed, 38 insertions(+), 78 deletions(-) diff --git a/MSCachedAsyncViewDrawing.h b/MSCachedAsyncViewDrawing.h index efd9489..2cf41fb 100644 --- a/MSCachedAsyncViewDrawing.h +++ b/MSCachedAsyncViewDrawing.h @@ -24,6 +24,7 @@ typedef void (^MSCachedAsyncViewDrawingCompletionBlock)(UIImage *drawnImage); * Once finished, it'll call the completion block on the main thread with the drawn UIImage object. * `MSCachedAsyncViewDrawing` objects keep an internal cache so multiple calls to this method with the same `cacheKey` * will result in the immediate call of `completionBlock`. + * @param `synchronous` Passing YES will run synchronously and will call completionBlock before returning nil * @param `cacheKey` make sure you create a string with the paremeters of the view. Two views configured * differently (say, different text or font color) should have different cache keys to avoid collisions) * @param backgroundColor if you want a transparent image, just pass [UIColor clearColor]. @@ -33,21 +34,11 @@ typedef void (^MSCachedAsyncViewDrawingCompletionBlock)(UIImage *drawnImage); * @return NSOperation associated with the drawing. If you're enqueuing a lot of drawing, you may want to cancel the operation * before it finishes if the result is not needed anymore to save resources. */ -- (NSOperation *)drawViewAsyncWithCacheKey:(NSString *)cacheKey - size:(CGSize)imageSize - backgroundColor:(UIColor *)backgroundColor - drawBlock:(MSCachedAsyncViewDrawingDrawBlock)drawBlock - completionBlock:(MSCachedAsyncViewDrawingCompletionBlock)completionBlock; - -/** - * @discussion this is the synchronous version of the other method. - * It waits until the image is loaded and returns it instead of calling a completion block. - * @param `drawBlock` is called on the caller thread. - */ -- (UIImage *)drawViewSyncWithCacheKey:(NSString *)cacheKey - size:(CGSize)imageSize - backgroundColor:(UIColor *)backgroundColor - drawBlock:(MSCachedAsyncViewDrawingDrawBlock)drawBlock; - +- (NSOperation *)drawViewSynchronous:(BOOL)synchronous + withCacheKey:(NSString *)cacheKey + size:(CGSize)imageSize + backgroundColor:(UIColor *)backgroundColor + drawBlock:(MSCachedAsyncViewDrawingDrawBlock)drawBlock + completionBlock:(MSCachedAsyncViewDrawingCompletionBlock)completionBlock; @end \ No newline at end of file diff --git a/MSCachedAsyncViewDrawing.m b/MSCachedAsyncViewDrawing.m index 3b7950b..3610c0b 100644 --- a/MSCachedAsyncViewDrawing.m +++ b/MSCachedAsyncViewDrawing.m @@ -24,16 +24,18 @@ + (_MSViewDrawingOperation *)viewDrawingBlockOperationWithBlock:(void (^)(_MSVie @end +typedef void (^MSCachedAsyncViewDrawingOperationBlock)(_MSViewDrawingOperation *operation); + @implementation _MSViewDrawingOperation -+ (_MSViewDrawingOperation *)viewDrawingBlockOperationWithBlock:(void (^)(_MSViewDrawingOperation *))block ++ (_MSViewDrawingOperation *)viewDrawingBlockOperationWithBlock:(MSCachedAsyncViewDrawingOperationBlock)operationBlock { _MSViewDrawingOperation *operation = [[self alloc] init]; __weak _MSViewDrawingOperation *weakOperation = operation; [operation addExecutionBlock:^{ - block(weakOperation); + operationBlock(weakOperation); }]; return operation; @@ -80,12 +82,12 @@ - (id)init #pragma mark - Private -- (NSOperation *)drawViewWithCacheKey:(NSString *)cacheKey - size:(CGSize)imageSize - backgroundColor:(UIColor *)backgroundColor - drawBlock:(MSCachedAsyncViewDrawingDrawBlock)drawBlock - completionBlock:(MSCachedAsyncViewDrawingCompletionBlock)completionBlock - waitUntilDone:(BOOL)waitUntilDone +- (NSOperation *)drawViewSynchronous:(BOOL)synchronous + withCacheKey:(NSString *)cacheKey + size:(CGSize)imageSize + backgroundColor:(UIColor *)backgroundColor + drawBlock:(MSCachedAsyncViewDrawingDrawBlock)drawBlock + completionBlock:(MSCachedAsyncViewDrawingCompletionBlock)completionBlock; { UIImage *cachedImage = [self.cache objectForKey:cacheKey]; @@ -98,28 +100,28 @@ - (NSOperation *)drawViewWithCacheKey:(NSString *)cacheKey MSCachedAsyncViewDrawingDrawBlock heapDrawBlock = [drawBlock copy]; MSCachedAsyncViewDrawingCompletionBlock heapCompletionBlock = [completionBlock copy]; - _MSViewDrawingOperation *operation = [_MSViewDrawingOperation viewDrawingBlockOperationWithBlock:[^(_MSViewDrawingOperation *operation) { + MSCachedAsyncViewDrawingOperationBlock operationBlock = ^(_MSViewDrawingOperation *operation) { if (operation.isCancelled) { return; } - + BOOL opaque = [self colorIsOpaque:backgroundColor]; - + UIGraphicsBeginImageContextWithOptions(imageSize, opaque, 0); - + if (operation.isCancelled) { UIGraphicsEndImageContext(); return; } - + CGContextRef context = UIGraphicsGetCurrentContext(); - + CGRect rectToDraw = (CGRect){.origin = CGPointZero, .size = imageSize}; - + BOOL shouldDrawBackgroundColor = ![backgroundColor isEqual:[UIColor clearColor]]; - + if (shouldDrawBackgroundColor) { CGContextSaveGState(context); @@ -129,29 +131,31 @@ - (NSOperation *)drawViewWithCacheKey:(NSString *)cacheKey } CGContextRestoreGState(context); } - + heapDrawBlock(rectToDraw); - + if (operation.isCancelled) { UIGraphicsEndImageContext(); return; } - + UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext(); - + UIGraphicsEndImageContext(); - + if (operation.isCancelled) { UIGraphicsEndImageContext(); return; } - + [self.cache setObject:resultImage forKey:cacheKey]; operation.resultImage = resultImage; - } copy]]; + }; + + _MSViewDrawingOperation *operation = [_MSViewDrawingOperation viewDrawingBlockOperationWithBlock:[operationBlock copy]]; __strong __block _MSViewDrawingOperation *_operation = operation; @@ -162,54 +166,19 @@ - (NSOperation *)drawViewWithCacheKey:(NSString *)cacheKey }); }; - [self.operationQueue addOperation:operation]; - - if (waitUntilDone) + if (synchronous) { - [operation waitUntilFinished]; + operationBlock(operation); + completionBlock(operation.resultImage); return nil; } else { + [self.operationQueue addOperation:operation]; return operation; } } -#pragma mark - Public - -- (NSOperation *)drawViewAsyncWithCacheKey:(NSString *)cacheKey - size:(CGSize)imageSize - backgroundColor:(UIColor *)backgroundColor - drawBlock:(MSCachedAsyncViewDrawingDrawBlock)drawBlock - completionBlock:(MSCachedAsyncViewDrawingCompletionBlock)completionBlock -{ - return [self drawViewWithCacheKey:cacheKey - size:imageSize - backgroundColor:backgroundColor - drawBlock:drawBlock - completionBlock:completionBlock - waitUntilDone:NO]; -} - -- (UIImage *)drawViewSyncWithCacheKey:(NSString *)cacheKey - size:(CGSize)imageSize - backgroundColor:(UIColor *)backgroundColor - drawBlock:(MSCachedAsyncViewDrawingDrawBlock)drawBlock -{ - __block UIImage *image = nil; - - [self drawViewWithCacheKey:cacheKey - size:imageSize - backgroundColor:backgroundColor - drawBlock:drawBlock - completionBlock:^(UIImage *drawnImage) { - image = drawnImage; - } - waitUntilDone:YES]; - - return image; -} - #pragma mark - Aux - (BOOL)colorIsOpaque:(UIColor *)color