From 209ec5274562098469337a4e064ee65368614b40 Mon Sep 17 00:00:00 2001 From: Michael Potter Date: Tue, 22 Oct 2013 18:43:29 -0700 Subject: [PATCH] Use ARC - Fix various memory leaks - Remove unnecessary code because of ARC - Update README --- FastImageCache/FICImageCache.h | 2 +- FastImageCache/FICImageCache.m | 23 +----- FastImageCache/FICImageFormat.m | 9 +-- FastImageCache/FICImageTable.h | 2 +- FastImageCache/FICImageTable.m | 73 +++++-------------- FastImageCache/FICImageTableChunk.h | 4 +- FastImageCache/FICImageTableChunk.m | 27 +------ FastImageCache/FICImageTableEntry.m | 8 +- FastImageCache/FICUtilities.m | 4 +- FastImageCacheDemo/Classes/FICDAppDelegate.h | 4 +- FastImageCacheDemo/Classes/FICDAppDelegate.m | 15 +--- .../FICDFullscreenPhotoDisplayController.h | 2 +- .../FICDFullscreenPhotoDisplayController.m | 35 +++------ FastImageCacheDemo/Classes/FICDPhoto.h | 4 +- FastImageCacheDemo/Classes/FICDPhoto.m | 17 +---- .../Classes/FICDPhotosTableViewCell.h | 2 +- .../Classes/FICDPhotosTableViewCell.m | 12 +-- FastImageCacheDemo/Classes/FICDTableView.m | 7 +- .../Classes/FICDViewController.m | 26 +++---- .../project.pbxproj | 2 + README.md | 15 ++-- 21 files changed, 78 insertions(+), 215 deletions(-) diff --git a/FastImageCache/FICImageCache.h b/FastImageCache/FICImageCache.h index e36375b..2a2e65b 100644 --- a/FastImageCache/FICImageCache.h +++ b/FastImageCache/FICImageCache.h @@ -34,7 +34,7 @@ typedef void (^FICImageRequestCompletionBlock)(UIImage *sourceImage); @discussion The delegate is responsible for asynchronously providing the source image for an entity. Optionally, the delegate can require that all formats in a format family for a particular entity be processed. Any errors that occur in the image cache are also communicated back to the delegate. */ -@property(nonatomic, assign) id delegate; +@property (nonatomic, weak) id delegate; ///--------------------------------------- /// @name Accessing the Shared Image Cache diff --git a/FastImageCache/FICImageCache.m b/FastImageCache/FICImageCache.m index 93a0a27..c0d18da 100644 --- a/FastImageCache/FICImageCache.m +++ b/FastImageCache/FICImageCache.m @@ -26,7 +26,7 @@ @interface FICImageCache () { NSMutableDictionary *_formats; NSMutableDictionary *_imageTables; NSMutableDictionary *_requests; - id _delegate; + __weak id _delegate; BOOL _delegateImplementsShouldProcessAllFormatsInFamilyForEntity; BOOL _delegateImplementsErrorDidOccurWithMessage; @@ -83,14 +83,6 @@ - (id)init { return self; } -- (void)dealloc { - [_formats release]; - [_imageTables release]; - [_requests release]; - - [super dealloc]; -} - #pragma mark - Working with Formats - (void)setFormats:(NSArray *)formats { @@ -106,7 +98,6 @@ - (void)setFormats:(NSArray *)formats { // Only initialize an image table for this format if it is needed on the current device. FICImageTable *imageTable = [[FICImageTable alloc] initWithFormat:imageFormat]; [_imageTables setObject:imageTable forKey:formatName]; - [imageTable release]; [_formats setObject:imageFormat forKey:formatName]; [imageTableFiles addObject:[[imageTable tableFilePath] lastPathComponent]]; @@ -144,7 +135,7 @@ - (NSArray *)formatsWithFamily:(NSString *)family { } } - return [[formats copy] autorelease]; + return [formats copy]; } #pragma mark - Retrieving Images @@ -175,8 +166,6 @@ - (BOOL)_retrieveImageForEntity:(id )entity withFormatName:(NSString completionBlock(entity, formatName, image); }); } - - [image release]; // Already retained by the block }); } else { UIImage *image = [imageTable newImageForEntityUUID:entityUUID sourceImageUUID:sourceImageUUID]; @@ -192,8 +181,6 @@ - (BOOL)_retrieveImageForEntity:(id )entity withFormatName:(NSString }); } } - - [image release]; // Already retained by the block }; if (image == nil && _delegate != nil) { @@ -274,7 +261,7 @@ static void _FICAddCompletionBlockForEntity(NSString *formatName, NSMutableDicti [completionBlocks setObject:blocksArray forKey:formatName]; } - FICImageCacheCompletionBlock completionBlockCopy = [[completionBlock copy] autorelease]; + FICImageCacheCompletionBlock completionBlockCopy = [completionBlock copy]; [blocksArray addObject:completionBlockCopy]; } } @@ -286,7 +273,7 @@ - (void)setImage:(UIImage *)image forEntity:(id )entity withFormatNam NSDictionary *completionBlocksDictionary = nil; if (completionBlock != nil) { - completionBlocksDictionary = [NSDictionary dictionaryWithObject:[NSArray arrayWithObject:[[completionBlock copy] autorelease]] forKey:formatName]; + completionBlocksDictionary = [NSDictionary dictionaryWithObject:[NSArray arrayWithObject:[completionBlock copy]] forKey:formatName]; } NSString *entityUUID = [entity UUID]; @@ -360,8 +347,6 @@ - (void)_processImage:(UIImage *)image forEntity:(id )entity imageTab } }); } - - [resultImage release]; }); } } diff --git a/FastImageCache/FICImageFormat.m b/FastImageCache/FICImageFormat.m index ac8c7d9..20519bd 100644 --- a/FastImageCache/FICImageFormat.m +++ b/FastImageCache/FICImageFormat.m @@ -61,7 +61,7 @@ - (void)setImageSize:(CGSize)imageSize { #pragma mark - Object Lifecycle + (instancetype)formatWithName:(NSString *)name family:(NSString *)family imageSize:(CGSize)imageSize isOpaque:(BOOL)isOpaque maximumCount:(NSInteger)maximumCount devices:(FICImageFormatDevices)devices { - FICImageFormat *imageFormat = [[[FICImageFormat alloc] init] autorelease]; + FICImageFormat *imageFormat = [[FICImageFormat alloc] init]; [imageFormat setName:name]; [imageFormat setFamily:family]; @@ -73,13 +73,6 @@ + (instancetype)formatWithName:(NSString *)name family:(NSString *)family imageS return imageFormat; } -- (void)dealloc { - [_name release]; - [_family release]; - - [super dealloc]; -} - #pragma mark - Working with Dictionary Representations - (NSDictionary *)dictionaryRepresentation { diff --git a/FastImageCache/FICImageTable.h b/FastImageCache/FICImageTable.h index d9bcbc4..2e71022 100644 --- a/FastImageCache/FICImageTable.h +++ b/FastImageCache/FICImageTable.h @@ -42,7 +42,7 @@ extern NSString *const FICImageTableScreenScaleKey; /** The image format that describes the image table. */ -@property (nonatomic, retain, readonly) FICImageFormat *imageFormat; +@property (nonatomic, strong, readonly) FICImageFormat *imageFormat; ///----------------------------------------------- /// @name Accessing Information about Image Tables diff --git a/FastImageCache/FICImageTable.m b/FastImageCache/FICImageTable.m index f329210..ee08bc1 100644 --- a/FastImageCache/FICImageTable.m +++ b/FastImageCache/FICImageTable.m @@ -49,7 +49,7 @@ @interface FICImageTable () { size_t _chunkLength; NSInteger _chunkCount; - CFMutableDictionaryRef _chunkDictionary; + NSMapTable *_chunkMapTable; NSMutableArray *_recentChunks; NSRecursiveLock *_lock; @@ -104,9 +104,9 @@ + (NSString *)directoryPath { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); - __directoryPath = [[[paths objectAtIndex:0] stringByAppendingPathComponent:@"ImageTables"] retain]; + __directoryPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"ImageTables"]; - NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease]; + NSFileManager *fileManager = [[NSFileManager alloc] init]; BOOL directoryExists = [fileManager fileExistsAtPath:__directoryPath]; if (directoryExists == NO) { [fileManager createDirectoryAtPath:__directoryPath withIntermediateDirectories:YES attributes:nil error:nil]; @@ -128,7 +128,7 @@ - (instancetype)initWithFormat:(FICImageFormat *)imageFormat { _lock = [[NSRecursiveLock alloc] init]; _imageFormat = [imageFormat copy]; - _imageFormatDictionary = [[imageFormat dictionaryRepresentation] retain]; + _imageFormatDictionary = [imageFormat dictionaryRepresentation]; _screenScale = [[UIScreen mainScreen] scale]; @@ -136,7 +136,7 @@ - (instancetype)initWithFormat:(FICImageFormat *)imageFormat { _imageRowLength = FICByteAlignForCoreAnimation([_imageFormat pixelSize].width * bytesPerPixel); _imageLength = _imageRowLength * (NSInteger)[_imageFormat pixelSize].height; - _chunkDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL); // Non-retained keys and values + _chunkMapTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory]; _indexMap = [[NSMutableDictionary alloc] init]; _occupiedIndexes = [[NSMutableIndexSet alloc] init]; @@ -176,8 +176,7 @@ - (instancetype)initWithFormat:(FICImageFormat *)imageFormat { // If something goes wrong and we can't open the image table file, then we have no choice but to release and nil self. NSString *message = [NSString stringWithFormat:@"*** FIC Error: %s could not open the image table file at path %@. The image table was not created.", __PRETTY_FUNCTION__, _filePath]; [[FICImageCache sharedImageCache] _logMessage:message]; - - [self release]; + self = nil; } } @@ -190,54 +189,32 @@ - (instancetype)init { } - (void)dealloc { - [_imageFormat release]; - [_filePath release]; - - CFRelease(_chunkDictionary); - - [_indexMap release]; - [_occupiedIndexes release]; - [_MRUEntries release]; - [_sourceImageMap release]; - [_imageFormatDictionary release]; - [_recentChunks release]; - if (_fileDescriptor >= 0) { close(_fileDescriptor); } - - [_lock release]; - - [super dealloc]; } #pragma mark - Working with Chunks - (FICImageTableChunk *)_cachedChunkAtIndex:(NSInteger)index { - return (FICImageTableChunk *)CFDictionaryGetValue(_chunkDictionary, (const void *)index); + FICImageTableChunk *cachedChunk = [_chunkMapTable objectForKey:@(index)]; + + return cachedChunk; } - (void)_setChunk:(FICImageTableChunk *)chunk index:(NSInteger)index { if (chunk != nil) { - CFDictionarySetValue(_chunkDictionary, (const void *)index, (const void *)chunk); + [_chunkMapTable setObject:chunk forKey:@(index)]; } else { - CFDictionaryRemoveValue(_chunkDictionary, (const void *)index); + [_chunkMapTable removeObjectForKey:@(index)]; } } -- (void)_cleanupRecentChunks { - [_lock lock]; - - [_recentChunks removeAllObjects]; - - [_lock unlock]; -} - - (FICImageTableChunk *)_chunkAtIndex:(NSInteger)index { FICImageTableChunk *chunk = nil; if (index < _chunkCount) { - chunk = [[self _cachedChunkAtIndex:index] retain]; + chunk = [self _cachedChunkAtIndex:index]; if (chunk == nil) { size_t chunkLength = _chunkLength; @@ -246,7 +223,7 @@ - (FICImageTableChunk *)_chunkAtIndex:(NSInteger)index { chunkLength = _fileLength - chunkOffset; } - chunk = [[FICImageTableChunk alloc] initWithImageTable:self fileDescriptor:_fileDescriptor index:index length:chunkLength]; + chunk = [[FICImageTableChunk alloc] initWithFileDescriptor:_fileDescriptor index:index length:chunkLength]; [self _setChunk:chunk index:index]; } @@ -260,15 +237,7 @@ - (FICImageTableChunk *)_chunkAtIndex:(NSInteger)index { } } - return [chunk autorelease]; -} - -- (void)_chunkWillBeDeallocated:(FICImageTableChunk *)chunk { - [_lock lock]; - - [self _setChunk:nil index:[chunk index]]; - - [_lock unlock]; + return chunk; } #pragma mark - Storing, Retrieving, and Deleting Entries @@ -351,11 +320,9 @@ - (UIImage *)newImageForEntityUUID:(NSString *)entityUUID sourceImageUUID:(NSStr } else { [self _entryWasAccessedWithEntityUUID:entityUUID]; - [entryData retain]; // Released by _FICReleaseImageData - // Create CGImageRef whose backing store *is* the mapped image table entry. We avoid a memcpy this way. CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - CGDataProviderRef dataProvider = CGDataProviderCreateWithData((void *)entryData, [entryData bytes], [entryData imageLength], _FICReleaseImageData); + CGDataProviderRef dataProvider = CGDataProviderCreateWithData((__bridge_retained void *)entryData, [entryData bytes], [entryData imageLength], _FICReleaseImageData); CGSize pixelSize = [_imageFormat pixelSize]; CGBitmapInfo bitmapInfo; @@ -386,8 +353,7 @@ - (UIImage *)newImageForEntityUUID:(NSString *)entityUUID sourceImageUUID:(NSStr } static void _FICReleaseImageData(void *info, const void *data, size_t size) { - FICImageTableEntry *entryData = (FICImageTableEntry *)info; - [entryData release]; + CFRelease(info); } - (void)deleteEntryForEntityUUID:(NSString *)entityUUID { @@ -452,7 +418,7 @@ - (void)_setEntryCount:(NSInteger)entryCount { } else { _fileLength = fileLength; _entryCount = entryCount; - _chunkCount = (_entryCount + _entriesPerChunk - 1) / _entriesPerChunk; + _chunkCount = _entriesPerChunk > 0 ? ((_entryCount + _entriesPerChunk - 1) / _entriesPerChunk) : 0; } } } @@ -478,7 +444,7 @@ - (FICImageTableEntry *)_entryDataAtIndex:(NSInteger)index { [_lock unlock]; - return [entryData autorelease]; + return entryData; } - (NSInteger)_nextEntryIndex { @@ -489,7 +455,6 @@ - (NSInteger)_nextEntryIndex { if (index == NSNotFound) { index = _entryCount; } - [unoccupiedIndexes release]; if (index >= [_imageFormat maximumCount] && [_MRUEntries count]) { // Evict the oldest/least-recently accessed entry here @@ -533,10 +498,8 @@ - (void)_entryWasAccessedWithEntityUUID:(NSString *)entityUUID { if (index == NSNotFound) { [_MRUEntries insertObject:entityUUID atIndex:0]; } else if (index != 0) { - [entityUUID retain]; [_MRUEntries removeObjectAtIndex:index]; [_MRUEntries insertObject:entityUUID atIndex:0]; - [entityUUID release]; } } diff --git a/FastImageCache/FICImageTableChunk.h b/FastImageCache/FICImageTableChunk.h index b93f6c1..4990fc9 100644 --- a/FastImageCache/FICImageTableChunk.h +++ b/FastImageCache/FICImageTableChunk.h @@ -42,8 +42,6 @@ /** Initializes a new image table chunk. - - @param imageTable The image table to create a chunk from. @param fileDescriptor The image table's file descriptor to map from. @@ -53,6 +51,6 @@ @return A new image table chunk. */ -- (instancetype)initWithImageTable:(FICImageTable *)imageTable fileDescriptor:(int)fileDescriptor index:(NSInteger)index length:(size_t)length; +- (instancetype)initWithFileDescriptor:(int)fileDescriptor index:(NSInteger)index length:(size_t)length; @end \ No newline at end of file diff --git a/FastImageCache/FICImageTableChunk.m b/FastImageCache/FICImageTableChunk.m index 66feed0..8478c67 100644 --- a/FastImageCache/FICImageTableChunk.m +++ b/FastImageCache/FICImageTableChunk.m @@ -7,22 +7,12 @@ // #import "FICImageTableChunk.h" -#import "FICImageTable.h" #import -#pragma mark FICImageTable (FICImageTableChunkAdditions) - -@interface FICImageTable (FICImageTableChunkAdditions) - -- (void)_chunkWillBeDeallocated:(FICImageTableChunk *)chunk; - -@end - #pragma mark - Class Extension @interface FICImageTableChunk () { - FICImageTable *_owningImageTable; NSInteger _index; void *_bytes; size_t _length; @@ -40,11 +30,10 @@ @implementation FICImageTableChunk #pragma mark - Object Lifecycle -- (id)initWithImageTable:(FICImageTable *)imageTable fileDescriptor:(int)fileDescriptor index:(NSInteger)index length:(size_t)length { +- (instancetype)initWithFileDescriptor:(int)fileDescriptor index:(NSInteger)index length:(size_t)length { self = [super init]; if (self != nil) { - _owningImageTable = [imageTable retain]; _index = index; _length = length; _fileOffset = _index * _length; @@ -59,23 +48,9 @@ - (id)initWithImageTable:(FICImageTable *)imageTable fileDescriptor:(int)fileDes } - (void)dealloc { - [_owningImageTable release]; - if (_bytes != NULL) { munmap(_bytes, _length); } - - [super dealloc]; -} - -- (oneway void)release { - // While it is good practice to never access retainCount, in this case, it is necessary. This is the only way - // to know that self will soon be deallocated prior to the start of execution of the dealloc method. - if ([self retainCount] == 1) { - [_owningImageTable _chunkWillBeDeallocated:self]; - } - - [super release]; } @end diff --git a/FastImageCache/FICImageTableEntry.m b/FastImageCache/FICImageTableEntry.m index 1219a2f..8497d61 100644 --- a/FastImageCache/FICImageTableEntry.m +++ b/FastImageCache/FICImageTableEntry.m @@ -60,7 +60,7 @@ - (id)initWithImageTableChunk:(FICImageTableChunk *)imageTableChunk bytes:(void self = [super init]; if (self != nil) { - _imageTableChunk = [imageTableChunk retain]; + _imageTableChunk = imageTableChunk; _bytes = bytes; _length = length; } @@ -68,12 +68,6 @@ - (id)initWithImageTableChunk:(FICImageTableChunk *)imageTableChunk bytes:(void return self; } -- (void)dealloc { - [_imageTableChunk release]; - - [super dealloc]; -} - #pragma mark - Other Accessors + (NSInteger)metadataVersion { diff --git a/FastImageCache/FICUtilities.m b/FastImageCache/FICUtilities.m index 438d4c5..2bb524d 100644 --- a/FastImageCache/FICUtilities.m +++ b/FastImageCache/FICUtilities.m @@ -33,11 +33,11 @@ inline size_t FICByteAlignForCoreAnimation(size_t bytesPerRow) { CFUUIDRef UUIDRef = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, UUIDBytes); if (UUIDRef != NULL) { - UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef); + UUIDString = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef); CFRelease(UUIDRef); } - return [UUIDString autorelease]; + return UUIDString; } CFUUIDBytes FICUUIDBytesWithString(NSString *string) { diff --git a/FastImageCacheDemo/Classes/FICDAppDelegate.h b/FastImageCacheDemo/Classes/FICDAppDelegate.h index 19e3ee8..e946626 100644 --- a/FastImageCacheDemo/Classes/FICDAppDelegate.h +++ b/FastImageCacheDemo/Classes/FICDAppDelegate.h @@ -10,7 +10,7 @@ @interface FICDAppDelegate : UIResponder -@property (nonatomic, retain) UIWindow *window; -@property (nonatomic, retain) FICDViewController *viewController; +@property (nonatomic, strong) UIWindow *window; +@property (nonatomic, strong) FICDViewController *viewController; @end diff --git a/FastImageCacheDemo/Classes/FICDAppDelegate.m b/FastImageCacheDemo/Classes/FICDAppDelegate.m index d837d11..31c8d12 100644 --- a/FastImageCacheDemo/Classes/FICDAppDelegate.m +++ b/FastImageCacheDemo/Classes/FICDAppDelegate.m @@ -21,15 +21,6 @@ @interface FICDAppDelegate () @implementation FICDAppDelegate -#pragma mark - Object Lifeycle - -- (void)dealloc { - [_window release]; - [_viewController release]; - - [super dealloc]; -} - #pragma mark - Application Lifecycle - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { @@ -62,11 +53,11 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( // Configure the window CGRect windowFrame = [[UIScreen mainScreen] bounds]; - UIWindow *window = [[[UIWindow alloc] initWithFrame:windowFrame] autorelease]; + UIWindow *window = [[UIWindow alloc] initWithFrame:windowFrame]; [self setWindow:window]; - UIViewController *rootViewController = [[[FICDViewController alloc] init] autorelease]; - UINavigationController *navigationController = [[[UINavigationController alloc] initWithRootViewController:rootViewController] autorelease]; + UIViewController *rootViewController = [[FICDViewController alloc] init]; + UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController]; [[self window] setRootViewController:navigationController]; [[self window] makeKeyAndVisible]; diff --git a/FastImageCacheDemo/Classes/FICDFullscreenPhotoDisplayController.h b/FastImageCacheDemo/Classes/FICDFullscreenPhotoDisplayController.h index 2cc8711..d6024a4 100644 --- a/FastImageCacheDemo/Classes/FICDFullscreenPhotoDisplayController.h +++ b/FastImageCacheDemo/Classes/FICDFullscreenPhotoDisplayController.h @@ -12,7 +12,7 @@ @interface FICDFullscreenPhotoDisplayController : NSObject -@property (nonatomic, assign) id delegate; +@property (nonatomic, weak) id delegate; + (instancetype)sharedDisplayController; diff --git a/FastImageCacheDemo/Classes/FICDFullscreenPhotoDisplayController.m b/FastImageCacheDemo/Classes/FICDFullscreenPhotoDisplayController.m index 45dbd3e..a215f17 100644 --- a/FastImageCacheDemo/Classes/FICDFullscreenPhotoDisplayController.m +++ b/FastImageCacheDemo/Classes/FICDFullscreenPhotoDisplayController.m @@ -12,7 +12,7 @@ #pragma mark Class Extension @interface FICDFullscreenPhotoDisplayController () { - id _delegate; + __weak id _delegate; BOOL _delegateImplementsWillShowSourceImageForPhotoWithThumbnailImageView; BOOL _delegateImplementsDidShowSourceImageForPhotoWithThumbnailImageView; BOOL _delegateImplementsWillHideSourceImageForPhotoWithThumbnailImageView; @@ -96,28 +96,18 @@ - (id)init { } - (void)dealloc { - [_fullscreenView release]; - [_backgroundView release]; - [_thumbnailImageView release]; - [_originalThumbnailImageViewSuperview release]; - [_sourceImageView release]; - [_photo release]; - [_tapGestureRecognizer setDelegate:nil]; - [_tapGestureRecognizer release]; - - [super dealloc]; } #pragma mark - Showing and Hiding a Fullscreen Photo - (void)showFullscreenPhoto:(FICDPhoto *)photo withThumbnailImageView:(UIImageView *)thumbnailImageView { // Stash away the photo - _photo = [photo retain]; + _photo = photo; // Stash away original thumbnail image view information - _thumbnailImageView = [thumbnailImageView retain]; - _originalThumbnailImageViewSuperview = [[thumbnailImageView superview] retain]; + _thumbnailImageView = thumbnailImageView; + _originalThumbnailImageViewSuperview = [thumbnailImageView superview]; _originalThumbnailImageViewFrame = [thumbnailImageView frame]; _originalThumbnailImageViewSubviewIndex = [[[thumbnailImageView superview] subviews] indexOfObject:thumbnailImageView]; @@ -183,24 +173,21 @@ - (void)hideFullscreenPhoto { [_fullscreenView removeFromSuperview]; - // Clean up photo ownership - [_photo release]; + // Inform the delegate that we just hide a fullscreen photo + if (_delegateImplementsDidHideSourceImageForPhotoWithThumbnailImageView) { + [_delegate photoDisplayController:self didHideSourceImage:sourceImage forPhoto:_photo withThumbnailImageView:_thumbnailImageView]; + } + + // Clean up _photo = nil; - // Clean up thumbnail image view ownership - [_thumbnailImageView release]; _thumbnailImageView = nil; - - [_originalThumbnailImageViewSuperview release]; _originalThumbnailImageViewSuperview = nil; _originalThumbnailImageViewFrame = CGRectZero; _originalThumbnailImageViewSubviewIndex = 0; - // Inform the delegate that we just hide a fullscreen photo - if (_delegateImplementsDidHideSourceImageForPhotoWithThumbnailImageView) { - [_delegate photoDisplayController:self didHideSourceImage:sourceImage forPhoto:_photo withThumbnailImageView:_thumbnailImageView]; - } + _sourceImageView.image = nil; }]; } diff --git a/FastImageCacheDemo/Classes/FICDPhoto.h b/FastImageCacheDemo/Classes/FICDPhoto.h index 14fb7d9..624aab8 100644 --- a/FastImageCacheDemo/Classes/FICDPhoto.h +++ b/FastImageCacheDemo/Classes/FICDPhoto.h @@ -19,8 +19,8 @@ extern CGSize const FICDPhotoPixelImageSize; @interface FICDPhoto : NSObject @property (nonatomic, copy) NSURL *sourceImageURL; -@property (nonatomic, retain, readonly) UIImage *sourceImage; -@property (nonatomic, retain, readonly) UIImage *thumbnailImage; +@property (nonatomic, strong, readonly) UIImage *sourceImage; +@property (nonatomic, strong, readonly) UIImage *thumbnailImage; @property (nonatomic, assign, readonly) BOOL thumbnailImageExists; // Methods for demonstrating more conventional caching techniques diff --git a/FastImageCacheDemo/Classes/FICDPhoto.m b/FastImageCacheDemo/Classes/FICDPhoto.m index a08ef42..6b556c6 100644 --- a/FastImageCacheDemo/Classes/FICDPhoto.m +++ b/FastImageCacheDemo/Classes/FICDPhoto.m @@ -57,16 +57,6 @@ - (BOOL)thumbnailImageExists { return thumbnailImageExists; } -#pragma mark - Object Lifecycle - -- (void)dealloc { - [_sourceImageURL release]; - [_UUID release]; - [_thumbnailFilePath release]; - - [super dealloc]; -} - #pragma mark - Image Helper Functions static CGMutablePathRef _FICDCreateRoundedRectPath(CGRect rect, CGFloat cornerRadius) { @@ -131,8 +121,9 @@ static CGMutablePathRef _FICDCreateRoundedRectPath(CGRect rect, CGFloat cornerRa - (NSString *)_thumbnailFilePath { if (!_thumbnailFilePath) { NSURL *photoURL = [self sourceImageURL]; - _thumbnailFilePath = [[NSTemporaryDirectory() stringByAppendingPathComponent:[[photoURL absoluteString] lastPathComponent]] retain]; + _thumbnailFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[photoURL absoluteString] lastPathComponent]]; } + return _thumbnailFilePath; } @@ -177,7 +168,7 @@ - (NSString *)UUID { if (_UUID == nil) { // MD5 hashing is expensive enough that we only want to do it once CFUUIDBytes UUIDBytes = FICUUIDBytesFromMD5HashOfString([_sourceImageURL absoluteString]); - _UUID = [FICStringWithUUIDBytes(UUIDBytes) retain]; + _UUID = FICStringWithUUIDBytes(UUIDBytes); } return _UUID; @@ -219,7 +210,7 @@ - (FICEntityImageDrawingBlock)drawingBlockForImage:(UIImage *)image withFormatNa } }; - return [[drawingBlock copy] autorelease]; + return drawingBlock; } @end diff --git a/FastImageCacheDemo/Classes/FICDPhotosTableViewCell.h b/FastImageCacheDemo/Classes/FICDPhotosTableViewCell.h index 06d582e..9a896d9 100644 --- a/FastImageCacheDemo/Classes/FICDPhotosTableViewCell.h +++ b/FastImageCacheDemo/Classes/FICDPhotosTableViewCell.h @@ -12,7 +12,7 @@ @interface FICDPhotosTableViewCell : UITableViewCell -@property (nonatomic, assign) id delegate; +@property (nonatomic, weak) id delegate; @property (nonatomic, assign) BOOL usesImageTable; @property (nonatomic, copy) NSArray *photos; diff --git a/FastImageCacheDemo/Classes/FICDPhotosTableViewCell.m b/FastImageCacheDemo/Classes/FICDPhotosTableViewCell.m index de8b7ae..03181a2 100644 --- a/FastImageCacheDemo/Classes/FICDPhotosTableViewCell.m +++ b/FastImageCacheDemo/Classes/FICDPhotosTableViewCell.m @@ -14,7 +14,7 @@ #pragma mark Class Extension @interface FICDPhotosTableViewCell () { - id _delegate; + __weak id _delegate; NSArray *_photos; NSMutableArray *_imageViews; @@ -36,7 +36,6 @@ @implementation FICDPhotosTableViewCell - (void)setPhotos:(NSArray *)photos { if (photos != _photos) { - [_photos release]; _photos = [photos copy]; // Either create the image views for this cell or clear them out if they already exist @@ -48,7 +47,6 @@ - (void)setPhotos:(NSArray *)photos { UIImageView *imageView = [[UIImageView alloc] init]; [imageView setContentMode:UIViewContentModeScaleAspectFill]; [_imageViews addObject:imageView]; - [imageView release]; } } else { for (UIImageView *imageView in _imageViews) { @@ -85,7 +83,7 @@ + (NSString *)reuseIdentifier { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - __reuseIdentifier = [NSStringFromClass([FICDPhotosTableViewCell class]) retain]; + __reuseIdentifier = NSStringFromClass([FICDPhotosTableViewCell class]); }); return __reuseIdentifier; @@ -127,13 +125,7 @@ - (id)init { } - (void)dealloc { - [_photos release]; - [_imageViews release]; - [_tapGestureRecognizer setDelegate:nil]; - [_tapGestureRecognizer release]; - - [super dealloc]; } #pragma mark - Configuring the View Hierarchy diff --git a/FastImageCacheDemo/Classes/FICDTableView.m b/FastImageCacheDemo/Classes/FICDTableView.m index 97ea688..05e1921 100644 --- a/FastImageCacheDemo/Classes/FICDTableView.m +++ b/FastImageCacheDemo/Classes/FICDTableView.m @@ -33,9 +33,6 @@ @implementation FICDTableView - (void)dealloc { [_displayLink invalidate]; - [_displayLink release]; - - [super dealloc]; } - (void)didMoveToWindow { @@ -43,7 +40,6 @@ - (void)didMoveToWindow { [self _scrollingStatusDidChange]; } else { [_displayLink invalidate]; - [_displayLink release]; _displayLink = nil; } } @@ -65,9 +61,10 @@ - (void)_scrollingStatusDidChange { if (isScrolling) { if (_displayLink == nil) { - _displayLink = [[CADisplayLink displayLinkWithTarget:self selector:@selector(_screenDidUpdateWhileScrolling:)] retain]; + _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_screenDidUpdateWhileScrolling:)]; [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:UITrackingRunLoopMode]; } + _framesInLastInterval = 0; _lastLogTime = CFAbsoluteTimeGetCurrent(); [_displayLink setPaused:NO]; diff --git a/FastImageCacheDemo/Classes/FICDViewController.m b/FastImageCacheDemo/Classes/FICDViewController.m index a2984b8..aef1c37 100644 --- a/FastImageCacheDemo/Classes/FICDViewController.m +++ b/FastImageCacheDemo/Classes/FICDViewController.m @@ -49,7 +49,6 @@ - (id)init { FICDPhoto *photo = [[FICDPhoto alloc] init]; [photo setSourceImageURL:imageURL]; [photos addObject:photo]; - [photo release]; } while ([photos count] < 5000) { @@ -71,23 +70,15 @@ - (id)init { - (void)dealloc { [_tableView setDelegate:nil]; [_tableView setDataSource:nil]; - [_tableView release]; - - [_photos release]; [_noImagesAlertView setDelegate:nil]; - [_noImagesAlertView release]; - - [_averageFPSLabel release]; - - [super dealloc]; } #pragma mark - View Controller Lifecycle - (void)loadView { CGRect viewFrame = [[UIScreen mainScreen] bounds]; - UIView *view = [[[UIView alloc] initWithFrame:viewFrame] autorelease]; + UIView *view = [[UIView alloc] initWithFrame:viewFrame]; [view setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; [view setBackgroundColor:[UIColor whiteColor]]; @@ -115,10 +106,10 @@ - (void)loadView { // Configure the navigation item UINavigationItem *navigationItem = [self navigationItem]; - UIBarButtonItem *resetBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Reset" style:UIBarButtonItemStyleBordered target:self action:@selector(_reset)] autorelease]; + UIBarButtonItem *resetBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Reset" style:UIBarButtonItemStyleBordered target:self action:@selector(_reset)]; [navigationItem setLeftBarButtonItem:resetBarButtonItem]; - UISegmentedControl *segmentedControl = [[[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:@"Conventional", @"Image Table", nil]] autorelease]; + UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:@"Conventional", @"Image Table", nil]]; [segmentedControl setSelectedSegmentIndex:0]; [segmentedControl addTarget:self action:@selector(_segmentedControlValueChanged:) forControlEvents:UIControlEventValueChanged]; [segmentedControl setSegmentedControlStyle:UISegmentedControlStyleBar]; @@ -135,7 +126,7 @@ - (void)loadView { [_tableView addObserver:self forKeyPath:@"averageFPS" options:NSKeyValueObservingOptionNew context:NULL]; } - UIBarButtonItem *averageFPSLabelBarButtonItem = [[[UIBarButtonItem alloc] initWithCustomView:_averageFPSLabel] autorelease]; + UIBarButtonItem *averageFPSLabelBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:_averageFPSLabel]; [navigationItem setRightBarButtonItem:averageFPSLabelBarButtonItem]; } @@ -292,7 +283,10 @@ - (void)_segmentedControlValueChanged:(UISegmentedControl *)segmentedControl { // Create an image from the bitmap context CGImageRef colorAveragedImageRef = CGBitmapContextCreateImage(bitmapContextRef); UIImage *colorAveragedImage = [UIImage imageWithCGImage:colorAveragedImageRef]; + + CGColorSpaceRelease(colorSpaceRef); CGImageRelease(colorAveragedImageRef); + CGContextRelease(bitmapContextRef); return colorAveragedImage; } @@ -302,7 +296,7 @@ static BOOL _FICDImageIsLight(UIImage *image) { CGImageRef imageRef = [image CGImage]; CGDataProviderRef dataProviderRef = CGImageGetDataProvider(imageRef); - NSData *pixelData = (NSData *)CGDataProviderCopyData(dataProviderRef); + NSData *pixelData = (__bridge_transfer NSData *)CGDataProviderCopyData(dataProviderRef); if ([pixelData length] > 0) { const UInt8 *pixelBytes = [pixelData bytes]; @@ -383,7 +377,7 @@ - (void)_displayAverageFPS:(CGFloat)averageFPS { averageFPSColor = [UIColor colorWithHue:(6 / 359.0) saturation:0.99 brightness:0.89 alpha:1]; // Red } - NSMutableAttributedString *mutableAttributedString = [[[NSMutableAttributedString alloc] initWithString:displayString] autorelease]; + NSMutableAttributedString *mutableAttributedString = [[NSMutableAttributedString alloc] initWithString:displayString]; [mutableAttributedString addAttribute:NSForegroundColorAttributeName value:averageFPSColor range:NSMakeRange(0, averageFPSStringLength)]; [_averageFPSLabel setAttributedText:mutableAttributedString]; @@ -414,7 +408,7 @@ - (UITableViewCell*)tableView:(UITableView*)table cellForRowAtIndexPath:(NSIndex FICDPhotosTableViewCell *tableViewCell = (FICDPhotosTableViewCell *)[table dequeueReusableCellWithIdentifier:reuseIdentifier]; if (tableViewCell == nil) { - tableViewCell = [[[FICDPhotosTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier] autorelease]; + tableViewCell = [[FICDPhotosTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]; [tableViewCell setBackgroundColor:[table backgroundColor]]; [tableViewCell setSelectionStyle:UITableViewCellSelectionStyleNone]; } diff --git a/FastImageCacheDemo/FastImageCacheDemo.xcodeproj/project.pbxproj b/FastImageCacheDemo/FastImageCacheDemo.xcodeproj/project.pbxproj index 22a6789..6134fbe 100644 --- a/FastImageCacheDemo/FastImageCacheDemo.xcodeproj/project.pbxproj +++ b/FastImageCacheDemo/FastImageCacheDemo.xcodeproj/project.pbxproj @@ -265,6 +265,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; @@ -297,6 +298,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; diff --git a/README.md b/README.md index f0f7aed..f1efecb 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,8 @@ A significant burden on performance for graphics-rich applications like [Path](h ## Version History -- [**1.0**](http://github.com/path/FastImageCache/tree/1.0) (10/18/2013): Initial release +- [**1.0**](https://github.com/path/FastImageCache/releases/tag/1.0) (10/18/2013): Initial release +- [**1.1**](https://github.com/path/FastImageCache/releases/tag/1.1) (10/22/2013): Added ARC support and more robust Core Animation byte alignment ## What Fast Image Cache Does @@ -97,7 +98,7 @@ There are obvious consequences to this approach, however. Uncompressed image dat #### Byte Alignment -For high-performance scrolling, it is critical that Core Animation is able to use an image without first having to create a copy. One of the reasons Core Animation would create a copy of an image is improper byte-alignment of the image's underlying [`CGImageRef`](http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&ved=0CCwQFjAA&url=http%3A%2F%2Fdeveloper.apple.com%2Flibrary%2Fios%2Fdocumentation%2Fgraphicsimaging%2FReference%2FCGImage%2FReference%2Freference.html&ei=fG9XUpX_BqWqigLymIG4BQ&usg=AFQjCNHTelntXU5Gw0BQkQqj9HC5iZibyA&sig2=tLY7PDhyockUVlVFbrzyOQ). A properly aligned bytes-per-row value must be a multiple of `8 pixels × bytes per pixel`. For a typical ARGB image, the aligned bytes-per-row value is a multiple of 32. Every image table is configured such that each image is always properly byte-aligned for Core Animation from the start. As a result, when images are retrieved from an image table, they are already in a form that Core Animation can work with directly without having to create a copy. +For high-performance scrolling, it is critical that Core Animation is able to use an image without first having to create a copy. One of the reasons Core Animation would create a copy of an image is improper byte-alignment of the image's underlying [`CGImageRef`](http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&ved=0CCwQFjAA&url=http%3A%2F%2Fdeveloper.apple.com%2Flibrary%2Fios%2Fdocumentation%2Fgraphicsimaging%2FReference%2FCGImage%2FReference%2Freference.html&ei=fG9XUpX_BqWqigLymIG4BQ&usg=AFQjCNHTelntXU5Gw0BQkQqj9HC5iZibyA&sig2=tLY7PDhyockUVlVFbrzyOQ). A properly aligned bytes-per-row value must be a multiple of `8 pixels × bytes per pixel`. For a typical ARGB image, the aligned bytes-per-row value is a multiple of 64. Every image table is configured such that each image is always properly byte-aligned for Core Animation from the start. As a result, when images are retrieved from an image table, they are already in a form that Core Animation can work with directly without having to create a copy. ## Considerations @@ -127,13 +128,13 @@ Image format families can be specified to efficiently make use of a single sourc ## Requirements -Fast Image Cache requires iOS 5.0 or greater and relies on the following frameworks: +Fast Image Cache requires iOS 6.0 or greater and relies on the following frameworks: - Foundation - Core Graphics - UIKit -> **Note**: Fast Image Cache does **not** use ARC. If your project uses ARC, you must configure Xcode to [disable ARC for Fast Image Cache source files](http://stackoverflow.com/questions/6646052/how-can-i-disable-arc-for-a-single-file-in-a-project). +> **Note**: As of version 1.1, Fast Image Cache **does** use ARC. --- @@ -163,7 +164,7 @@ Before the image cache can be used, it needs to be configured. This must occur e Each image format corresponds to an image table that the image cache will use. Image formats that can use the same source image to render the images they store in their image tables should belong to the same [image format family](#working-with-image-format-families). See [Image Table Size](#image-table-size) for more information about how to determine an appropriate maximum count. ```objective-c -FICImageFormat *smallUserThumbnailImageFormat = [[[FICImageFormat alloc] init] autorelease]; +FICImageFormat *smallUserThumbnailImageFormat = [[FICImageFormat alloc] init]; smallUserThumbnailImageFormat.name = XXImageFormatNameUserThumbnailSmall; smallUserThumbnailImageFormat.family = XXImageFormatFamilyUserThumbnails; smallUserThumbnailImageFormat.imageSize = CGSizeMake(50, 50); @@ -171,7 +172,7 @@ smallUserThumbnailImageFormat.opaque = YES; smallUserThumbnailImageFormat.maximumCount = 250; smallUserThumbnailImageFormat.devices = FICImageFormatDevicePhone; -FICImageFormat *mediumUserThumbnailImageFormat = [[[FICImageFormat alloc] init] autorelease]; +FICImageFormat *mediumUserThumbnailImageFormat = [[FICImageFormat alloc] init]; mediumUserThumbnailImageFormat.name = XXImageFormatNameUserThumbnailMedium; mediumUserThumbnailImageFormat.family = XXImageFormatFamilyUserThumbnails; mediumUserThumbnailImageFormat.imageSize = CGSizeMake(100, 100); @@ -244,7 +245,7 @@ Here is an example implementation of the [`FICEntity`](https://s3.amazonaws.com/ UIGraphicsPopContext(); }; - return [[drawingBlock copy] autorelease]; + return drawingBlock; } ```