IOS network image cache details
When developing mobile applications, such as Android and IOS, our mobile applications target the Internet and frequently access the network due to factors such as mobile phone traffic, network speed, and memory, network optimization is particularly important.
For example, if an application needs to display network images frequently, it will not be able to download images from the Internet each time. This will take too much time and traffic, and the network image will be cached, the following are some of my opinions on IOS network image caching and some shortcomings. You are welcome to point them out and discuss them together.
Steps for processing the network image cache:
1. Check whether the memory has this image based on the image URL. If yes, return the image. If NO, go to step 2.
2. Check whether the physical storage has this image. If yes, return the image. If NO, go to step 3.
3. Download the image from the network, save it to the memory and physical storage, and return the image.
Note: Because the URL contains special characters and the length is unknown, MD5 processing or other processing should be performed on the URL.
The following describes the code for the above steps:
1. Memory Cache Image Processing
Use NSMutableDictionary to store the image UIImage. The Key of the array is the URL address of the image.
// Cache images to memory
[Plain]View plaincopy
- [MemCache setObject: image forKey: key];
2. Physical cache Image Processing
If you keep the image on a physical storage device, you can directly use NSFileManager to save the URL as a file name.
3. online image download Processing
The image is downloaded asynchronously. After the download, the image is saved to NSMutableDictionary and physical storage.
The following is a class from SDWebImageleik network image cache processing., With detailed notes
. H file
[Plain]View plaincopy
- @ Interface SDImageCache: NSObject
- {
- NSMutableDictionary * memCache; // memory cache image reference
- NSString * diskCachePath; // physical cache path
- NSOperationQueue * cacheInQueue, * cacheOutQueue;
- }
-
- + (SDImageCache *) nvidimagecache;
-
- // Save the image
- -(Void) storeImage :( UIImage *) image forKey :( NSString *) key;
-
- // Save the image and select whether to save it to the physical storage.
- -(Void) storeImage :( UIImage *) image forKey :( NSString *) key toDisk :( BOOL) toDisk;
-
- // Save the image. You can save NSData to physical storage.
- -(Void) storeImage :( UIImage *) image imageData :( NSData *) data forKey :( NSString *) key toDisk :( BOOL) toDisk;
-
- // Return UIImage with key
- -(UIImage *) imageFromKey :( NSString *) key;
-
- // If an error occurred while obtaining the memory image, can the image be searched on the physical storage?
- -(UIImage *) imageFromKey :( NSString *) key fromDisk :( BOOL) fromDisk;
-
-
- -(Void) queryDiskCacheForKey :( NSString *) key delegate :( id ) Delegate userInfo :( NSDictionary *) info;
-
- // Clear the key index image
- -(Void) removeImageForKey :( NSString *) key;
- // Clear memory image
- -(Void) clearMemory;
- // Clear the physical Cache
- -(Void) clearDisk;
- // Clear expired physical Cache
- -(Void) cleanDisk;
-
- @ End
. M file[Plain]View plaincopy
- @ Implementation SDImageCache
-
- # Pragma mark NSObject
-
- -(Id) init
- {
- If (self = [super init])
- {
- // Init the memory cache
- MemCache = [[NSMutableDictionary alloc] init];
-
- // Init the disk cache
- NSArray * paths = NSSearchPathForDirectoriesInDomains (NSCachesDirectory, NSUserDomainMask, YES );
- DiskCachePath = [[[paths objectAtIndex: 0] stringByAppendingPathComponent: @ ImageCache] retain];
-
- If (! [[NSFileManager defaultManager] fileExistsAtPath: diskCachePath])
- {
- [[NSFileManager defaultManager] createDirectoryAtPath: diskCachePath
- WithIntermediateDirectories: YES
- Attributes: nil
- Error: NULL];
- }
-
- // Init the operation queue
- CacheInQueue = [[NSOperationQueue alloc] init];
- CacheInQueue. maxConcurrentOperationCount = 1;
- CacheOutQueue = [[NSOperationQueue alloc] init];
- CacheOutQueue. maxConcurrentOperationCount = 1;
-
- # If TARGET_ OS _IPHONE
- // Subscribe to app events
- [[Nsicationcenter center defacenter center] addObserver: self
- Selector: @ selector (clearMemory)
- Name: UIApplicationDidReceiveMemoryWarningNotification
- Object: nil];
-
- [[Nsicationcenter center defacenter center] addObserver: self
- Selector: @ selector (cleanDisk)
- Name: UIApplicationWillTerminateNotification
- Object: nil];
-
- # If _ IPHONE_ OS _VERSION_MIN_REQUIRED> = _ IPHONE_4_0
- UIDevice * device = [UIDevice currentDevice];
- If ([device respondsToSelector: @ selector (isMultitaskingSupported)] & device. multitaskingSupported)
- {
- // When in background, clean memory in order to have less chance to be killed
- [[Nsicationcenter center defacenter center] addObserver: self
- Selector: @ selector (clearMemory)
- Name: UIApplicationDidEnterBackgroundNotification
- Object: nil];
- }
- # Endif
- # Endif
- }
-
- Return self;
- }
-
- -(Void) dealloc
- {
- [MemCache release], memCache = nil;
- [DiskCachePath release], diskCachePath = nil;
- [CacheInQueue release], cacheInQueue = nil;
-
- [[Nsicationcenter center defacenter center] removeObserver: self];
-
- [Super dealloc];
- }
-
- # Pragma mark SDImageCache (class methods)
-
- + (SDImageCache *) nvidimagecache
- {
- If (instance = nil)
- {
- Instance = [[SDImageCache alloc] init];
- }
-
- Return instance;
- }
-
- # Pragma mark SDImageCache (private)
-
- /*
- * Create a path for the specified image key
- */
- -(NSString *) cachePathForKey :( NSString *) key
- {
- Const char * str = [key UTF8String];
- Unsigned char r [CC_MD5_DIGEST_LENGTH];
- CC_MD5 (str, (CC_LONG) strlen (str), r );
- NSString * filename = [NSString stringWithFormat: @ % 02x % 02x % 02x % 02x % 02x % 02x % 02x % 02x % 02x % 02x % 02x % 02x % 02x % 02x % 02x % 02x,
- R [0], r [1], r [2], r [3], r [4], r [5], r [6], r [7], r [8], r [9], r [10], r [11], r [12], r [13], r [14], r [15];
-
- Return [diskCachePath stringByAppendingPathComponent: filename];
- }
-
- /*
- * Save key and Data to physical storage
- * KeyAndData [0]-> key
- * KeyAndData [1]-> Data
- */
- -(Void) storeKeyWithDataToDisk :( NSArray *) keyAndData
- {
- // Can't use defaultManager another thread
- NSFileManager * fileManager = [[NSFileManager alloc] init];
-
- NSString * key = [keyAndData objectAtIndex: 0];
- NSData * data = [keyAndData count]> 1? [KeyAndData objectAtIndex: 1]: nil;
-
- // If data exists, it is saved to the physical storage.
- If (data)
- {
- [FileManager createFileAtPath: [self cachePathForKey: key] contents: data attributes: nil];
- }
- Else
- {
- // If no data exists, convert the UIImage to JPEG and save it to the physical storage.
- // If no data representation given, convert the UIImage in JPEG and store it
- // This trick is more CPU/memory intensive and doesn' t preserve alpha channel
- UIImage * image = [[self imageFromKey: key fromDisk: YES] retain]; // be thread safe with no lock
- If (image)
- {
- # If TARGET_ OS _IPHONE
- [FileManager createFileAtPath: [self cachePathForKey: key] contents: UIImageJPEGRepresentation (image, (CGFloat) 1.0) attributes: nil];
- # Else
- NSArray * representations = [image representations];
- NSData * upload data = [NSBitmapImageRep representationOfImageRepsInArray: representations usingType: ns1_filetype properties: nil];
- [FileManager createFileAtPath: [self cachePathForKey: key] contents: external data attributes: nil];
- # Endif
- [Image release];
- }
- }
-
- [FileManager release];
- }
-
- /*
- * Search for image Delegation
- */
- -(Void) policydelegate :( NSDictionary *) arguments
- {
- NSString * key = [arguments objectForKey: @ key];
- Id Delegate = [arguments objectForKey: @ delegate];
- NSDictionary * info = [arguments objectForKey: @ userInfo];
- UIImage * image = [arguments objectForKey: @ image];
-
- If (image)
- {
- [MemCache setObject: image forKey: key];
-
- If ([delegate respondsToSelector: @ selector (imageCache: didFindImage: forKey: userInfo :)])
- {
- [Delegate imageCache: self didFindImage: image forKey: key userInfo: info];
- }
- }
- Else
- {
- If ([delegate respondsToSelector: @ selector (imageCache: didNotFindImageForKey: userInfo :)])
- {
- [Delegate imageCache: self didNotFindImageForKey: key userInfo: info];
- }
- }
- }
-
- /*
- * Search for images in the physical Cache
- */
- -(Void) queryDiskCacheOperation :( NSDictionary *) arguments
- {
- NSString * key = [arguments objectForKey: @ key];
- NSMutableDictionary * mutableArguments = [[arguments mutableCopy] autorelease];
-
- UIImage * image = [[UIImage alloc] initWithContentsOfFile: [self cachePathForKey: key] autorelease];
- If (image)
- {
- # Ifdef ENABLE_SDWEBIMAGE_DECODER
- UIImage * decodedImage = [UIImage decodedImageWithImage: image];
- If (decodedImage)
- {
- Image = decodedImage;
- }
- # Endif
- [MutableArguments setObject: image forKey: @ image];
- }
-
- [Self defined mselecw.mainthread: @ selector (policydelegate :) withObject: mutableArguments waitUntilDone: NO];
- }
-
- # Pragma mark ImageCache
-
- /*
- * Cache Images
- *
- **/
- -(Void) storeImage :( UIImage *) image imageData :( NSData *) data forKey :( NSString *) key toDisk :( BOOL) toDisk
- {
- If (! Image |! Key)
- {
- Return;
- }
-
- // Cache images to memory
- [MemCache setObject: image forKey: key];
-
- // If the data needs to be cached to the physical storage and is not empty, the data will be cached to the physical storage.
- If (toDisk)
- {
- If (! Data) return;
- NSArray * keyWithData;
- If (data)
- {
- KeyWithData = [NSArray arrayWithObjects: key, data, nil];
- }
- Else
- {
- KeyWithData = [NSArray arrayWithObjects: key, nil];
- }
- // The background thread caches images to physical storage.
- [CacheInQueue addOperation: [[NSInvocationOperation alloc] initWithTarget: self
- Selector: @ selector (storeKeyWithDataToDisk :)
- Object: keyWithData] autorelease];
- }
- }
-
- /*
- * Save images to memory instead of physical storage
- */
- -(Void) storeImage :( UIImage *) image forKey :( NSString *) key
- {
- [Self storeImage: image imageData: nil forKey: key toDisk: YES];
- }
- /*
- * Save images to memory instead of physical storage
- */
- -(Void) storeImage :( UIImage *) image forKey :( NSString *) key toDisk :( BOOL) toDisk
- {
- [Self storeImage: image imageData: nil forKey: key toDisk: toDisk];
- }
-
- /*
- * Return the specified image using the key.
- */
- -(UIImage *) imageFromKey :( NSString *) key
- {
- Return [self imageFromKey: key fromDisk: YES];
- }
-
- /*
- * Returns an image.
- * Key: the key of the image.
- * FromDisk: If there is no image in the memory, check whether it is found on the physical storage.
- * Return returns the searched image. If not, returns nil.
- */
- -(UIImage *) imageFromKey :( NSString *) key fromDisk :( BOOL) fromDisk
- {
- If (key = nil)
- {
- Return nil;
- }
-
- UIImage * image = [memCache objectForKey: key];
-
- If (! Image & fromDisk) // if there is no image in the memory and you can search for it on the physical storage, the image on the physical storage is returned.
- {
- Image = [[[UIImage alloc] initWithContentsOfFile: [self cachePathForKey: key] autorelease];
- If (image)
- {
- [MemCache setObject: image forKey: key];
- }
- }
-
- Return image;
- }
-
- -(Void) queryDiskCacheForKey :( NSString *) key delegate :( id ) Delegate userInfo :( NSDictionary *) info
- {
- If (! Delegate)
- {
- Return;
- }
-
- If (! Key)
- {
- If ([delegate respondsToSelector: @ selector (imageCache: didNotFindImageForKey: userInfo :)])
- {
- [Delegate imageCache: self didNotFindImageForKey: key userInfo: info];
- }
- Return;
- }
-
- // First check the in-memory cache...
- UIImage * image = [memCache objectForKey: key];
- If (image)
- {
- //... Your y delegate immediately, no need to go async
- If ([delegate respondsToSelector: @ selector (imageCache: didFindImage: forKey: userInfo :)])
- {
- [Delegate imageCache: self didFindImage: image forKey: key userInfo: info];
- }
- Return;
- }
-
- NSMutableDictionary * arguments = [NSMutableDictionary dictionaryWithCapacity: 3];
- [Arguments setObject: key forKey: @ key];
- [Arguments setObject: delegate forKey: @ delegate];
- If (info)
- {
- [Arguments setObject: info forKey: @ userInfo];
- }
- [CacheOutQueue addOperation: [[[NSInvocationOperation alloc] initWithTarget: self selector: @ selector (queryDiskCacheOperation :) object: arguments] autorelease];
- }
-
- /*
- * Remove a specified image from memory and physical storage
- */
- -(Void) removeImageForKey :( NSString *) key
- {
- If (key = nil)
- {
- Return;
- }
-
- [MemCache removeObjectForKey: key];
- [[NSFileManager defaultManager] removeItemAtPath: [self cachePathForKey: key] error: nil];
- }
- /*
- * Clear images in the memory cache area
- */
- -(Void) clearMemory
- {
- [CacheInQueue cancelAllOperations]; // won't be able to complete
- [MemCache removeAllObjects];
- }
-
- /*
- * Clear images stored in physical storage
- */
- -(Void) clearDisk
- {
- [CacheInQueue cancelAllOperations];
- [[NSFileManager defaultManager] removeItemAtPath: diskCachePath error: nil];
- [[NSFileManager defaultManager] createDirectoryAtPath: diskCachePath
- WithIntermediateDirectories: YES
- Attributes: nil
- Error: NULL];
- }
- /*
- * Clear expired cached images
- */
- -(Void) cleanDisk
- {
- NSDate * expirationDate = [NSDate dateWithTimeIntervalSinceNow:-cacheMaxCacheAge];
- NSDirectoryEnumerator * fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath: diskCachePath];
- For (NSString * fileName in fileEnumerator)
- {
- NSString * filePath = [diskCachePath stringByAppendingPathComponent: fileName];
- NSDictionary * attrs = [[NSFileManager defaultManager] attributesOfItemAtPath: filePath error: nil];
- If ([[[attrs fileModificationDate] laterDate: expirationDate] isEqualToDate: expirationDate])
- {
- [[NSFileManager defaultManager] removeItemAtPath: filePath error: nil];
- }
- }
- }
-
- @ End