iOS 內存警告的處理

更多內容請挪步我的博客

[LEAF Photo] 中要處理圖片,如果操作時間長的話會收到 Memory Warning,內存處理的不好的話就會崩潰,下面記錄下處理內存警告的一些優化。

處理內存警告

處理所有 ViewController 中的 didReceiveMemoryWarning 方法
didReceiveMemoryWarning 方法中應當把緩存的變量都清空,這些變量最好都是通過懶加載的方式創建,這樣收到內存警告后也會自動加載。

@interface TestViewController () 
@property (strong, nonatomic) NSMutableArray *dataArray;
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    self.dataArray = nil;
}
- (NSMutableArray *)dataArray {
    if (!_dataArray) {
        _dataArray = [[NSMutableArray alloc] init];
    }
    // 賦值處理 ... 
    return _dataArray;
}

在處理圖片的 Controller 中當用戶點擊某個按鈕時會加載一些 View,例如處理文字、背景、繪圖筆等是彈出的設置頁面,這些 View 都封裝成了自定義的類從 Controller 中分離出去,這些類的內存可以被處理,如果在 didReceiveMemoryWarning 中把這些視圖清理時,需要判斷當前視圖是否是否正在使用,否則可能出現正在操作這些視圖時,由于收到內存警告而被刪除導致錯誤,在 iOS 6 以上的版本中可以用如下方式判斷視圖是否在使用

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    if ([self.view window] == nil)   { // 視圖是否正在使用
       self.settingView = nil;
    }
}

另外,NSCache 緩存類會在收到 Memory Warning 時自動刪除緩存內容,不需要手動做清理,有個 Demo 點擊查看

還可以在 AppDelegate 中實現 applicationDidReceiveMemoryWarning 方法做一些全局數據清理

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
    
}

消耗內存的對象創建放到 AutoReleasePool 中

函數中出現很多中間變量占據大量內存,或者不多的中間變量但是也是占用較大內存的,需要放到自動釋放池中

   @autoreleasepool {
        ALAsset *asset = self.assetArray[i];
        if (asset && ![asset isEqual:[NSNull null]]) {
            UIImage *img = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage]];
            // ...
        }
    }

使用 Allocations 工具查看是否有應該釋放但是沒有被釋放的內存

如果進入一個頁面后退出該頁面,內存沒有降回或者接近降回到進入該頁面前的內存數,說明該頁面有內存沒有被釋放。

例如點擊 [PhotoBook] 中點擊添加照片的按鈕,添加照片后退出這個頁面后發現內存中駐留了該頁面的內存,而該段內存是存在 LTPhotoPickerViewController 的某個變量中的,查看頁面代碼發現添加圖片按鈕中有如下代碼,每次點擊都創建了相同的控制器,但是沒有對其釋放的地方,所以創建了多個重復的對象,而這個對象中保存了很多內存。

- (void)stickerPhotoAction:(id)sender {
    LTPhotoPickerViewController *pickerController = [[LTPhotoPickerViewController alloc] init];
    [self.navigationController pushViewController:pickerController animated:YES];
}

這種簡單的錯誤很容易修改,只是早期的時候腦袋壞掉不小心寫錯,要解決問題最困難的問題不是如何修改,而是如何找到問題在哪。

修改方法:將 pickerController 改為屬性,每次點擊按鈕的時候使用同樣的屬性對象即可。

循環引用

除了 Block 內部要注意不要引用自己,還要注意是否有屬性應當是 weak 的,但是設置成 strong,導致內部引用計數錯誤導致的無法釋放,這種現象也要靠 Allocations 工具檢查,頁面退出后還有對象被 Persistent,可以看看是否有該 weak 的被 strong 了。

如果使用照片盡量對其進行壓縮節省內存

// ALAsset *asset = ...;
UIImage *img = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage]]; 
NSData *imgData = UIImageJPEGRepresentation(fullScreenImage, 0.7);
UIImage *img = [UIImage imageWithData:imgData];
// scaled
UIImage *img = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage] scale:scale orientation:UIImageOrientationUp];

同樣是獲取相冊中的照片,兩種方式進行壓縮節省內存

圖片保存時注意保存圖片的大小

如果需要自己創建圖片上下文并繪制,給用戶選擇的圖片大小最大不要超過 4096 ** 4096 像素,可以給用戶大、中、小幾種選擇。

創建圖片上下文使用 UIGraphicsBeginImageContextWithOptions(size, YES, [UIScreen mainScreen].scale); 的話,在主流機上 scale 都是 2,那么創建出的圖片像素都是 size 大小的 2倍,如果讓用戶選擇 3200 ** 3200 大小的照片,保存后就是 6400 ** 6400,圖片這么大很容易造成內存吃緊,收到警告,所以不要使用 UIGraphicsBeginImageContextWithOptions 方式,保存圖片后看下圖片是否是想要的大小。

GCD Timer 的使用注意事項

NSTimer 一定要在恰當的地方執行 invalidate,否則會造成內存泄漏,但是用 GCD Timer 就可以繞過這個問題。但是在 [PhotoBook] 項目中添加 GCD Timer 后想要在某種情況下,把 Timer 暫停,于是設置了某個變量,當該變量在某種情況下將 timer 暫停,于是有了下面的代碼

dispatch_source_set_event_handler(timer, ^() {
    // if (condition)  {      
        // dispatch_suspend(timer);
   // }
}
dispatch_resume(timer);

發現在 dispatch_source_set_event_handler 中加上 dispatch_suspend,該頁面無法被釋放。具體分析請挪步這里

參考內容

25 iOS App Performance Tips Tricks

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 內存管理 簡述OC中內存管理機制。與retain配對使用的方法是dealloc還是release,為什么?需要與a...
    丶逐漸閱讀 2,013評論 1 16
  • IPhone下每個app可用的內存是被限制的,如果一個app使用的內存超過20M,則系統會向該app發送Memor...
    Yumazhiyao閱讀 3,789評論 1 11
  • 37.cocoa內存管理規則 1)當你使用new,alloc或copy方法創建一個對象時,該對象的保留計數器值為1...
    如風家的秘密閱讀 893評論 0 4
  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結起來就是把...
    Dove_iOS閱讀 27,217評論 30 472
  • 秋意斑駁,落葉雙雙, 醉了流年 夕陽殘影,凄風瑟瑟, 亂了青絲 腳步漸緩,眸定云遠去 南歸的鴻雁,敲開塵封的記憶 ...
    亮子_4ae7閱讀 247評論 0 0