iOS 內存泄漏


最近在做塗鴉小程序的時候,發現幾個內存問題。

塗鴉Demo這個程序打開后是進入到相冊選擇圖片,接着載入一個UIScrollView,然后在UIScrollView上添加一個UIImageView,再將選擇圖片設置為ImageView的Image。塗鴉的時候,將一個UIView加在UIImageView上,繪圖將在這個UIView上進行。

 

第一個問題

現在的問題是,只要開始塗鴉,內存就會從暴漲,漲幅跟圖片的大小有關,見下圖。

圖片大小 塗鴉前內存 塗鴉后內存 內存漲幅
21K 4.9M 8.1M 3.2M
104K 4.5M 29.5M 25M
563K 6.9M 35.1M 28M

 

 

 

 

為什么小圖片塗鴉前所占內存更大?大概是因為圖片較小時,對內存影響更大的因素可能是顏色等。104K的圖片偏黑,而21K的圖片偏亮。

經過不斷注釋代碼,發現內存暴漲的原因是,我在UIView的touchesMove函數中調用了[self setNeedsDisplay],即手指移動的每一個過程,都會讓界面重繪。那么就可以解釋上圖為什么內存的漲幅與圖片的大小相關,因為重繪的界面與圖片的大小相關。

目前繪圖的原理是,由於重繪的函數會清空上次的繪畫內容,所以每次重繪時需要將以前畫的軌跡重新繪一遍。舉個例子,假如畫一條線有3個點,則手指移動到第一點的時候,畫第一個點,手指移動到第二點的時候,將第一第二個點畫一下,手指移動到第三個點的時候,將第一第二第三個點都畫一下;同理,畫第二條線的時候,第一條線的每個點每時每刻都被重畫。

這種方法有個好處,就是撤銷功能很簡單,只要將最后一條線從記錄中刪除,再重繪一下,就好像撤銷了最新的那條線,其實是以前的所有線條被重畫了一次。

壞處顯而易見,耗時,耗CPU,耗內存。

所以現在,只能尋找一種可以保存上一次的繪畫內容又能完成撤銷功能的繪圖方法就應該能解決問題,待續……

 

第二個問題

在研究第一個問題的時候,發現第二個問題,就是從相冊中選擇圖片,進入預覽界面后內存會上升(正常,因為載入了圖片),再取消,回到相冊。此時內存不會變,這就有問題了,按理說預覽界面已經不存在,為什么內存不會降到跟選圖前一樣呢?

用XCode的Profile的Leak工具檢查了一下,發現內存泄漏的原因是UIStatusBarHideAnimationParameters和UIImage。

先看UIImage,檢查了一下代碼,發現一個隱藏得比較深的內存泄漏問題。下面是打開相冊的ViewController中的代碼,該代碼是在選擇照片后被調用。選擇照片后跳轉到預覽模式的ViewController。

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    /*選擇圖片后,獲得圖片*/
    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
    /*跳轉*/
    QLMomentMainViewController *viewController = [[QLMomentMainViewController alloc] initWithNibName:nil bundle:nil];
    viewController.image = image;
    [picker dismissViewControllerAnimated:YES completion:^(void){}];
    [self.navigationController pushViewController:viewController animated:YES];
    [viewController release];
}

注意紅色語句,首先該image不是第一個ViewController創建的(沒有一個包含alloc/new/copy/mutableCopy的方法),所以不需要它來釋放image;然后第二個ViewController也沒有一個包含alloc/new/copy/mutableCopy的方法來創建image,所以,我天真的以為,第二個ViewController不需要對這個image的釋放負責。所以沒有在第二個ViewController的dealloc函數中釋放該image。

其實這是錯誤的,因為viewController.image = image;這行代碼會讓image的引用計數+1,這個image是我們自己創建的property,所以在其隱含的setImage方法中對image retain了一次。所以第二個ViewController有責任在dealloc中release該image屬性。

OK,那么現在只要在第二個ViewController中的dealloc中release該image,問題解決。

 

第三個問題

剩下就是這個神秘的UIStatusBarHideAnimationParameters。

經不斷測試,發現出現這種情況的原因是,進入相冊,不選圖,取消之后出來,那么就會發生一次內存泄漏

剛開始還以為是我在工程的Info.plist文件中添加的用於隱藏狀態欄的原因

可是刪掉之后還是出現內存泄漏。於是只能上網求助。

后來發現,我重寫了完成選擇照片的函數

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info

卻沒有重寫取消選擇照片的函數

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker

該函數的描述中有這么一句:

Your delegate’s implementation of this method should dismiss the picker view by calling the dismissModalViewControllerAnimated: method of the parent view controller.

Implementation of this method is optional, but expected.

ok,那么重寫這個函數,並在里面調用[picker dismissViewControllerAnimated:YES completion:^(void){}];來讓相冊界面消失。

……

但是,問題依然存在!

StackOverFlow上有人遇到這個問題,有人建議:

Maybe you need to clear the delegate for your UIImagePickerController? Delegates can prevent objects from being properly deallocated. 

於是我在完成選擇照片或取消選擇照片的回調函數中添加一句pick.delegate = nil,

……

但是,問題依然存在!

然后,StackOverFlow有人說

It's a bug in the SDK. 

然后,下了一下蘋果的官方源碼——一個打開相冊選擇圖片的demo,發現竟然也會出現這個神秘的UIStatusBarHideAnimationParameters引發的內存泄漏!

最后,StackOverFlow有人建議

There is a know issue with the uiimagepickercontroller with memory leaks.

Apple recommend that you only allocate and instantiate only one instance and store it somewhere for the life of the application (whilst running that is).

所以目前只能把picker聲明為全局變量,避免多次alloc和release。

 

目前問題只能解決到這里了。

總結

1.記得必要時釋放你自己創建的property,不能一味依賴alloc,new,copy等字眼來決定是否release.

2.打開系統相冊再退出會發生內存泄漏,這是蘋果的Bug?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM