iOS提供了ARC功能,很大程度上簡化了內存管理的代碼。java
但使用ARC並不表明了不會發生內存泄露,使用不當照樣會發生內存泄露。objective-c
下面列舉兩種ARC致使內存泄露的狀況。app
1,循環參照ide
A有個屬性參照B,B有個屬性參照A,若是都是strong參照的話,兩個對象都沒法釋放。函數
這種問題常發生於把delegate聲明爲strong屬性了。動畫
例,atom
@interface SampleViewControllerspa
@property (nonatomic, strong) SampleClass *sampleClass;指針
@end調試
@interface SampleClass
@property (nonatomic, strong) SampleViewController *delegate;
@end
上例中,解決辦法是把SampleClass 的delegate屬性的strong改成assing便可。
2,死循環
若是某個ViewController中有無限循環,也會致使即便ViewController對應的view關掉了,ViewController也不能被釋放。
這種問題常發生於animation處理。
例,
好比,
CATransition *transition = [CATransition animation];
transition.duration = 0.5;
tansition.repeatCount = HUGE_VALL;
[self.view.layer addAnimation:transition forKey:"myAnimation"];
上例中,animation重複次數設成HUGE_VALL,一個很大的數值,基本上等於無限循環了。
解決辦法是,在ViewController關掉的時候,中止這個animation。
-(void)viewWillDisappear:(BOOL)animated {
[self.view.layer removeAllAnimations];
}
內存泄露的狀況固然不止以上兩種。
即便用了ARC,咱們也要深入理解iOS的內存管理機制,這樣纔能有效避免內存泄露。
實例一:
用arc和非arc混編,非arc的類在arc裏實例化而且使用,在arc裏竟然出現內存泄露,並且應爲是arc,因此沒法使用release,autorelease和dealloc去管理內存。正常狀況下應該是不會出現這種狀況的,某一個類如果ARC,則在這個類裏面都應該遵循ARC的用法,而無需關心用到的類是不是ARC的,一樣,在非ARC類裏面,就須要遵循內存管理原則。
用ARC,只是編譯器幫你管理了什麼時候去release,retain,不用ARC就須要你本身去管理,說到底只是誰去管理的問題,因此你再好好看看,可能問題與ARC無關。
若是實在找不到問題,建議你找到泄露的那個對象,將其賦值爲nil,由於ARC裏面,一旦對象沒有指針指向,就會立刻被釋放。
實例二:
最近在學objective-c,我發現建立項目時若是使用了ARC,很是容易內存泄露,常常某個對象已經被釋放掉了我還在使用,因爲不太瞭解這個機制,如今我舉出兩個例子,請經驗者幫我分析一下。
例子一:一開始,在AppDelegate.m的那個開始方法中時這樣寫的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
//
UITabBarController tabBarController = [[UITabBarController alloc] init];
[tabBarController setViewControllers:[self showConnectViewOnWindow]];
[tabBarController setDelegate:self];
//
[[self window] addSubview: [tabBarController view]];
[self.window makeKeyAndVisible];
return
YES;
}
|
而後,我還作了其餘的工做:tabBarController中有tabBarItem,點擊會調用一個方法
可是每次一點擊,就會報unrecognized selector send to instance的錯誤,
後來上網一查,說是要把tabBarController定義成全局變量,否則這個方法一結束,tabBarController就被釋放掉了,這樣點擊產生時間的對象都沒了,因而我把它定義成全局變量,確實能夠了,但個人疑問是,爲何方法一結束他就會釋放掉嗎,[[self window] addSubview: [tabBarController view]];我這一句不是已經在self window裏引用它了嗎,他怎麼還會被釋放,我以爲java和C#裏面這種狀況是不會釋放掉了。
1
2
3
4
5
6
7
8
|
例子二:在viewdidload方法裏面:
[self.navigationItem setTitle:Title];
leftButton = [[UIBarButtonItem alloc] initWithTitle:Cancel
style:UIBarButtonItemStyleBordered
target:self
action:
@selector
(CancleButtonClicked)];
self.navigationItem.leftBarButtonItem = leftButton;
|
這裏我給屏幕上方那個導航條加了一個左邊的按鈕,而後點擊這個按鈕後會用方法CancleButtonClicked來響應,可是我運行起來一點擊,仍是報unrecognized selector send to instances錯誤了,這裏又是哪一個對象釋放了,leftButton嗎?可是self.navigationItem.leftBarButtonItem = leftButton已經引用了啊。
解決方法:
例子一[[self window] addSubview: [tabBarController view]];
你只引用了tabBarController的view,沒有引用tabBarController
例子二,不知道什麼緣由,看看有沒有拼寫錯誤吧。
另外,我感受局部變量的內存通常只在它的生命週期內有效。出了它所定義的區域,即便不釋放,也最好不要用了。
自從使用了ARC,代碼寫起來方便了不少,不須要retain,release,dealloc了,可是也隱藏了一些釋放內存的問題。
self
進行
retain
,這有時候有可能會產生一個引用環(兩個或以上的對象之間直接或間接地互相引用)並致使內存泄露。解決的方法是:當須要在
Block中訪問實例變量的時候,建立一個指向
self
的指針,並對其使用
__block
修飾符,這樣
self
不會被自動
retain
:
在咱們代碼中關於block的使用能夠說隨處可見,第一次接觸block的時候是關於UIView的塊動畫,那時以爲block的使用好神奇, 再後來分析總結爲block其實就是一個c語言函數,只是咱們能夠在任意處調用此函數。有了這樣的理解我開始常用block。在作項目之後發現使用 block居然會引發內存泄露,因而開始本身調試研究block的內存管理問題。
這裏有一個簡單的block使用,在裏面咱們能夠添加任何本身想進行的操做,大部分的使用也是如此
1 void (^Block)(int) = ^(int num){ 2 //此處還可添加其餘代碼 .... 3 NSLog(@"int number is %d",num); 4 };
包括UIView的塊動畫也是如此使用,在這裏咱們定義了一個圖像視圖的位置及透明度的變化
1 [UIView animateWithDuration:2.0 animations:^(void){ 2 smallImage.frame = CGRectMake(150, 80, 30, 30); 3 } completion:^(BOOL finished) { 4 smallImage.alpha = 0;
這些block操做中,我一直都認爲block中的對象會在block使用後就被釋放(但UIView的操做好像是這麼作的)
直到我在項目中碰見這樣一個狀況:我設置有2個控制器first及second,其中second中包含一個block對象,而block的實 現是在first中實現(通常的block傳值都是這麼作)。而界面的推送是由first控制器push出second控制器。但當我second控制器 pop的時候,問題出現second控制器不走delloc方法,即pop後second控制器還存在沒有被銷燬(由於當時要作delloc中作一些操 做,才發現這個問題)!block示例代碼以下:
first控制器中block的實現
1 SecondViewController *secondVC = [[SecondViewController alloc] init]; 2 secondVC .block = ^(NSString *text){ 3 self.text = secondVC.text; 4 };
這麼一個簡單的傳值block使用,竟然能引發second控制器沒法釋放,因而研究其原理,併網上搜索資料,得出一個結論:second控制 器在block中被持有一次才致使其沒法釋放。由於block本質上是一個函數,而編譯器不知道你何時會調用block裏面的值,因此爲了確保編譯器 內secondVC不會被釋放,編譯器會自動對其進行一次持有(在自身類中使用block方法操做自身的成員屬性也會使本身的引用計算加1,形成沒法釋 放)。
其解決辦法也簡單 在外部添加一個弱引用對象指向須要在block中操做的對象,即__weak typeof(對象名) 別名= 對象名;
1 SecondViewController *secondVC = [[SecondViewController alloc] init]; 2 __weak typeof(secondVC) second = secondVC; 3 __weak typeof(self) vc = self; 4 2 secondVC .block = ^(NSString *text){ 5 3 vc.text = second.text; 6 4 };
這樣就可以有效的防止block使用引發的內存泄露問題。
另外在還在項目中碰見一個關於NSTimer的使用問題。咱們想到在控制器銷燬時同時中止NSTimer並置爲nil
1 -(void)dealloc { 2 [self.timer invalidate]; 3 self.timer = nil; 4 NSLog(@"%@ dealloc", NSStringFromClass([self class])); 5 }
然而控制器被pop後並無走此方法(又是內存泄露),因爲以前出現了Block內存泄露的問題,我就想是否是由於這個_timer加載的時候對self進行了一次持有
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerUp:) userInfo:nil repeats:YES];
進行調試測驗,果真是這裏出了問題,由於其對控制器持有了一次。因而我想到既然這樣那我乾脆就在viewWillDisappear()中作個判 斷,若是是pop控制器,我就先設置[self.timer invalidate]操做這樣控制器就會走dealloc()方法。後來再網上找資料發現了一個更簡單的解決辦法,即同Block的內存管理同樣使用弱 引用對象
1 __weak typeof(self) vc = self; 2 _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:vc selector:@selector(timerUp:) userInfo:nil repeats:YES];
這樣的解決辦法就要比我以前的要簡單多了,惟一須要注意的就是此處vc的做用域!