retain cycle产生情况
A和B两个对象,A持有B,B同时也持有A,A只有B释放之后才有可能释放,同样B只有A释放后才可能释放,当双方都在等待对方释放的时候, retain cycle就形成了,结果是,两个对象都永远不会被释放,最终内存泄露。
retain cycle的避免
一般情况下,防止出现retain cycle有两个方法:
- 保持子对象引用父对象时是若引用
- 将造成retain cycle的一个变量置为nil,break掉环即可
需要特别小心的是,在使用block时,block引用的变量会自动retain一次,来保证block的调用是有效的,但同时这种retain是隐式的,block本身又可以看做一个对象,也存在生命周期,也可以被持有,所以极易造成retain cycle。
比如:
Car *car = [[Car alloc]init];
car.name = ^{
[car Run];
[car release];// ARC中不需要release
};
分析以上代码:car与block互相持有,造成retain cycle。为了解除retain cycle可以做出如下修改:
Car *car = [[Car alloc]init];
car.name = ^{
[car run];
car.name = nil;
[car release];// ARC中不需要release
};
分析以上代码:car.name=nil,不会造成retain cycle。
由于在ARC中引入了* __block、__unsafe_unretained、__weak*、__strong用于修饰变量,注:
- __strong:赋值给这个变量的对象会自动被retain一次,如果在block中引用它,block也会retain它一次。
- __weak:类似于__unsafe_unretained,只是如果所持有的对象被释放后,变量会自动被设置为nil,不会被block retain,更为安全
- __block:变量能在block中被修改(值修改,而不是修改对象中的某一个属性,可以理解为修改指针的指向),会被自动retain。
- __unsafe_unretained:赋值给这个变量不会被retain,被他修饰的变量的存在但不能保证持有对象的可靠性,它可能已经被释放了,而且留下了一个不安全的指针。
上面产生retain cycle的代码也可修改为以下形式:
__block Car *car = [[Car alloc]init];
car.name = ^{
[car run];
car = nil;
};
Car *car = [[Car alloc]init];
__weak __typeof (car) weakcar = car;
car.name = ^{
__typeof (car) strongcar = weakcar;
[strongcar run];
};
以上是个人理解,如有不对的地方,请指出。