產(chǎn)生原因: 兩個強引用對象互相指向對方
強引用對象互相指向時,雙方的引用計數(shù)都+1,導致任何時候引用計數(shù)都不為0,始終無法釋放,無法釋放他們的內(nèi)存,即使已經(jīng)沒有變量持有他們
危害: 使內(nèi)存消耗過高,性能變差,app閃退等.
具體情況:
- 父類與子類
例如在使用UITableView時,傳一個Controller給cell使用,自定義 cell: TestTableViewCell,在cell里聲明一個UITableView的變量,在cellForRowAtIndexPath里將tableView傳入cell.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexpath{
TestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellIdentifier" forIndexPath:indexPath];
cell.tableView = tableView;
return cell;
}
在聲明tableView時,如果我們用strong來修飾的話就會造成循環(huán)引用
@interface TestTableViewCell: UITableViewCell
@property (nonatomic, weak) UITableView *tableView;
@end
2.Block
@property (nonatomic, copy) TestBlock completionHandler;
self.completionHandler = ^{
[self.property removeObserver: self forKeyPath: @"pathName"];
// self do something
}
block在copy時都會對block內(nèi)部用到的對象進行強引用,該類又將block作為自己屬性變量,block的方法體里又使用了該類,形成了環(huán)狀,造成了循環(huán)引用.
避免:
__weak typeof(self) weakself = self;
self.completionHandler = ^{
//和block外的self,指向一樣,但是是不同的變量
__strong typeof (weakself) strongSelf = weak self;
[strongSelf doSomething];
}
先將強引用的對象轉為弱引用指針,防止了block和對象之間循環(huán)引用,再在block里將弱指針轉化為強引用指針,防止了多線程和ARC環(huán)境下弱引用隨時被釋放的問題.
或者 改為Block傳參:
self.blk = ^ (UIViewController *vc){
NSLog(@"use property: %@", vc.name);
};
self.blk(self);
- Delegate:
我們在聲明代理時,會寫
@property (nonatomic, weak) id <TestDelegate> delegate;
如果我們不使用weak,而是使用了strong,那么兩個類之間的代理就會變成:
aViewController.delegate = self; //假設self為aViewController, a和delegate的引用計數(shù)均加1,a與delegate相互強指向,造成循環(huán)引用.
4.NSTimer
ViewController --強引用--> 屬性Timer --強引用--> Target(self = ViewController) --強引用--> ViewController
比如在ViewController B 中有一個Timer
@property (nonatomic, strong) NSTimer *timer;
創(chuàng)建并掛載到main runloop
self.timer = [NSTimer scheduledTimerWithTimerInterval: 1 target: self selector:@selector(timerAction:) userInfo:nil repeats: true];
然后退出Controller B的時候忘記關掉timer, Controller B將不會釋放, B與timer循環(huán)引用,因為創(chuàng)建timer時指向了self
避免:
在適當?shù)那闆r下,invalidate timer,因為timer在validate狀態(tài)下引用計數(shù)始終大于0
注意:在上面的方法中,repeats參數(shù)為YSE時,NSTimer會保留目標對象,等到自身失效才釋放.執(zhí)行完畢任務后,一次性的定時器會自動失效,重復性的定時器,需要手動調用invalidate方法才會失效
若單純的想在 Controller B 的dealloc方法中去釋放timer是不可能調用的,因為timer沒有釋放的話,controller B也是不能釋放的,dealloc方法不會被調用