最近在作一個iOS手機項目的時候,遇到一個奇怪的問題,這裏跟你們分享一下。緩存
1、問題重現app
一、啓動App後,經過http請求下載了一個1.jpg文件到Cache目錄下,下載成功以後,將圖片顯示在界面上;(圖1)async
二、此時殺掉進程,再次啓動App後,圖片能夠正常顯示,而後點擊一個按鈕刪除剛剛下載的圖片;(圖2)ide
三、此時,將App壓後臺,再喚起,原來顯示的圖片消失了!!!(圖3)atom
圖1: 圖2: 圖3:spa
這裏咱們先貼一下代碼,用代碼來講明問題:code
1 @implementation ViewController 2 3 - (void)viewDidLoad { 4 [super viewDidLoad]; 5 6 _imageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; 7 _imageView.backgroundColor = [UIColor blackColor]; 8 _imageView.contentMode = UIViewContentModeScaleToFill; 9 [self.view addSubview:_imageView]; 10 11 _clearButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 12 _clearButton.frame = CGRectMake(0, 0, 100, 30); 13 _clearButton.center = self.view.center; 14 [_clearButton setTitle:@"清理緩存" forState:UIControlStateNormal]; 15 [_clearButton addTarget:self action:@selector(clearCache) forControlEvents:UIControlEventTouchUpInside]; 16 [self.view addSubview:_clearButton]; 17 } 18 19 - (void)viewDidAppear:(BOOL)animated 20 { 21 [super viewDidAppear:animated]; 22 23 [self showImage]; 24 } 25 26 - (NSString *)imagePath 27 { 28 NSArray *pathcaches=NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); 29 NSString *cacheDirectory = [pathcaches objectAtIndex:0]; 30 return [cacheDirectory stringByAppendingString:@"1.jpg"]; 31 } 32 33 // 顯示圖片 34 - (void)showImage 35 { 36 NSString *imageUrl = @"http://pic2.desk.chinaz.com/file/201203/6/chuangyisjbz1_p.jpg"; 37 NSURL *imageURL = [NSURL URLWithString:imageUrl]; 38 NSString *imagePath = [self imagePath]; 39 40 UIImage *image = [UIImage imageWithContentsOfFile:imagePath]; 41 if (image) { 42 // 第二次啓動App,緩存文件存在時,經過[UIImage imageWithContentsOfFile:]初始化 43 _imageView.image = image; 44 } else { 45 // 第一次啓動APP,下載圖片成功後,經過[UIImage imageWithData:]初始化 46 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 47 NSData *data = [NSData dataWithContentsOfURL:imageURL]; 48 if (data) { 49 dispatch_async(dispatch_get_main_queue(), ^{ 50 [data writeToFile:imagePath atomically:YES]; 51 _imageView.image = [UIImage imageWithData:data]; 52 }); 53 } 54 }); 55 } 56 } 57 58 // 清理緩存 59 - (void)clearCache 60 { 61 [[NSFileManager defaultManager] removeItemAtPath:[self imagePath] error:nil]; 62 } 63 64 @end
2、緣由分析orm
產生這個緣由的核心緣由在於UIImage的初始化方法。對象
一、直接使用文件初始化圖片blog
UIImage *image = [UIImage imageWithContentsOfFile:imagePath]
當殺掉進程,第二次啓動App的時候,緩存文件存在,則採用上面的方式初始化圖片;清除緩存,壓後臺,再喚起,注意觀察Console區域,會發現以下提示信息:
Mar 9 20:52:02 Demo[38525] <Error>: ImageIO: CGImageReadCreateDataWithMappedFile 'open' failed '/Users/yanzhi/Library/Developer/CoreSimulator/Devices/30EE295B-C260-4A5E-9446-362D05D50C0B/data/Containers/Data/Application/94D96217-D6DB-4BFC-BFD7-60FB66EA7A9E/Library/Caches1.jpg' error = 2 (No such file or directory)
這裏,須要注意,壓後臺,再喚起,咱們並無再次執行imageView.image = image的操做,可是爲何圖片就沒有了呢?同時比較奇怪的是,爲何會輸出一個ImageIO錯誤。
注意一下關於[UIImage initWithContentsOfFile:]方法的官方文檔說明:
- (instancetype)initWithContentsOfFile:(NSString *)path Discussion This method loads the image data into memory and marks it as purgeable. If the data is purged and needs to be reloaded, the image object loads that data again from the specified path. 這個方法加載圖片數據到內存中並將其標記爲「可清除」。若是內存中圖片被清除,須要從新加載時,這個Image對象須要再次從指定的path中加載圖像數據。
解釋一下,[UIImage imageWithContentsOfFile:]沒有上面的說明信息,僅僅在[UIImage initWithContentsOfFile:]方法中有這段說明。
正如上面文檔說明,當咱們壓後臺的時候,內存中的Image對象是能夠清除,因而就被系統回收掉該內存空間;再次喚起的時候,這個Image對象會嘗試從新加載該Path所指向的文件;可是該文件已經被刪除掉,所以系統在從新加載圖片的時候,就出現了ImageIO的錯誤數據,因而界面也沒法再次展現該圖片。
咱們能夠理解爲:使用initWithContentsOfFile的時候,系統爲咱們的Image對象和Path指向的文件作了一個映射Map,當Image對象被清理掉後,須要再次使用該Image對象時,會自動從Path指向的文件中去讀取數據。
所以,當你們使用initWithContentsOfFile或imageWithContentsOfFile去初始化圖片的時候,切記注意你的圖片文件是否可能被清理掉!
二、使用NSData轉換初始化圖片
NSData *data = [NSData dataWithContentsOfFile:path]; UIImage *image = [UIImage imageWithData:data];
當使用NSData做爲一箇中間對象來轉換的時候,若是path文件被刪除了,可是對應的data對象並不會被清理掉,始終會在內存中,那麼由今生成Image對象也不會被清理。
你們能夠作一個實驗,使用這兩個方法替換上方代碼片斷中的[UIImage imageWithContentsOfFile:]方法,從新驗證一下,你會發現一樣的場景,圖片始終會正常顯示。
3、總結
一、initWithContentsOfFile和imageWithContentsOfFile生成的Image對象,用來一次性展現,不可被緩存;Image對象可能被系統自動清理掉,並由系統自動加載;
二、若是須要緩存該Image對象,慎重選擇UIImage的初始化方法。