产生原因: 两个强引用对象互相指向对方
强引用对象互相指向时,双方的引用计数都+1,导致任何时候引用计数都不为0,始终无法释放,无法释放他们的内存,即使已经没有变量持有他们
危害: 使内存消耗过高,性能变差,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来修饰的话就会造成循环引用
@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内部用到的对象进行强引用,该类又将block作为自己属性变量,block的方法体里又使用了该类,形成了环状,造成了循环引用.
避免:
__weak typeof(self) weakself = self;
self.completionHandler = ^{
//和block外的self,指向一样,但是是不同的变量
__strong typeof (weakself) strongSelf = weak self;
[strongSelf doSomething];
}
先将强引用的对象转为弱引用指针,防止了block和对象之间循环引用,再在block里将弱指针转化为强引用指针,防止了多线程和ARC环境下弱引用随时被释放的问题.
或者 改为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的引用计数均加1,a与delegate相互强指向,造成循环引用.
4.NSTimer
ViewController --强引用--> 属性Timer --强引用--> Target(self = ViewController) --强引用--> ViewController
比如在ViewController B 中有一个Timer
@property (nonatomic, strong) NSTimer *timer;
创建并挂载到main runloop
self.timer = [NSTimer scheduledTimerWithTimerInterval: 1 target: self selector:@selector(timerAction:) userInfo:nil repeats: true];
然后退出Controller B的时候忘记关掉timer, Controller B将不会释放, B与timer循环引用,因为创建timer时指向了self
避免:
在适当的情况下,invalidate timer,因为timer在validate状态下引用计数始终大于0
注意:在上面的方法中,repeats参数为YSE时,NSTimer会保留目标对象,等到自身失效才释放.执行完毕任务后,一次性的定时器会自动失效,重复性的定时器,需要手动调用invalidate方法才会失效
若单纯的想在 Controller B 的dealloc方法中去释放timer是不可能调用的,因为timer没有释放的话,controller B也是不能释放的,dealloc方法不会被调用
网友评论