内存管理的思考方式,ARC中weak、strong的区别,循环引用
1.内存管理的思考方式
第一个例子 “自己生成并持有对象”
id obj = [[NSObject alloc]init];
//(自己生成并持有对象) NSObject类生成的对象A(假如叫做A)被obj持有
[obj release];
//释放A
这里有个定义,用alloc/new/copy/mutableCopy方法生成的对象为自己生成,其他则为非自己生成。
第二个例子 “非自己生成对象被自己持有”
{
id obj = [NSMutableArray array];
/*[NSMutableArray array]生成的对象B(假如叫做B),但不被obj持有
这时直接调用[obj release];是不行的,会导致程序崩溃*/
[obj retain];
//此时持有B
[obj release];
//释放B
}
引用计数式内存管理分为以下四种
- 自己生成的对象,自己持有
- 非自己生成的对象,自己持有
- 自己持有的对象不再需要时释放
- 非自己持有的对象无法释放
2.ARC中weak、strong的区别
ARC为自动引用计数,从这以后,开发者就不用显式地调用retain、release这些方法了,因此会自动调用。
简单来说
strong(强引用)修饰的对象被赋值时会对其引用计数会加1,而weak(弱引用)修饰的对象被赋值时对其引用计数不做改变
像这样的代码
//id __strong就是被strong修饰的id类,其实id默认也是有__strong
//id __weak就是被weak修饰的id类
{
id __strong obj_strong = [[NSObject alloc]init];
//NSObject类生成的对象A(假如叫做A)被obj_strong持有,A的引用计数加1,以前是0,现在是1,因为被强引用
id __weak obj_weak = [[NSObject alloc]init];
/* NSObject类生成的对象B(假如叫做B)被obj_weak持有,由于obj_weak是弱引用,
此时B被遗弃。编译器此时也会抛出警告:
Assigning retained object to weak variable; object will be released after assignment,
意思就是“将保留对象分配给弱变量; 对象将在分配后释放”。
*/
}
/*到这里,obj_strong超出了它的作用域,释放所有的强引用,这时就释放了对对象A的强引用
对象A引用计数由1变为0,对象A被释放。*/
那么weak修饰的对象就没办法持有对象了吗,答案是否定的,可能你也想到,weak不能使对象引用计数发生变化,使原本引用计数为0的对象因此被抛弃,但weak可以持有引用计数为不为0的弱引用,并不会造成对象被抛弃,还是看代码吧
{
id __strong obj_strong = [[NSObject alloc]init];
现在是1,因为被强引用*/
id __weak obj_weak = obj_strong;
//obj_weak持有对象的弱引用,引用计数不发生变化,还为1
}
/*到这里,obj_strong超出了它的作用域,释放所有的强引用,这时就释放了对对象A的强
引用,
对象A引用计数由1变为0,对象A被释放。obj_weak超出它的作用域,弱引用失效。*/
这样就使weak修饰的对象也能正常持有对象了
趁热打铁,再看一个例子
id __weak obj_weak = nil;
{
id __strong obj_strong = [[NSObject alloc]init];
/*NSObject类生成的对象A(假如叫做A)被obj_strong持有,A的引用计数加1,
以前是0,现在是1,因为被强引用*/
obj_weak = obj_strong;
//obj_weak持有对象的弱引用,引用计数不发生变化,还为1
}
/*到这里,obj_strong超出了它的作用域,释放所有的强引用,这时就释放了对对象A的强引用,
对象A引用计数由1变为0,对象A被释放。obj_weak并超出它的作用域,弱引用还在,不过对象被释放掉了,
弱引用则自动失效且obj_weak被置为nil,以前是对对象A的弱引用。*/
那么到这里,我们可以对strong、weak有一些概念
strong
如果对象是“狗”,strong可以类比为“栓狗的绳子”,当狗被绳子拴着,即被strong修饰的对象持有,一根绳子代表引用计数为1,N根绳子代表引用计数为N,当strong修饰的对象超出其作用域会取消所有的强引用,即收回拴狗的绳子,当拴狗的绳子为0时,狗就跑了,即对象被释放了。
weak
如果对象是“狗”,weak可以类比为“小孩”,weak对对象的弱引用相当于“小孩看狗”,当狗不在了,即对象被释放,即使小孩还想看狗,即还没有超出自己的作用域,可狗确实是没有了,小孩则会离开,即weak对象被置为nil。
3.循环引用
@interface Text : NSObject
@property(nonatomic,strong) UILabel *label;
@end
- (UILabel *)setLabel:(UILabel *)obj
{
_label = obj;
}
以下为循环引用
{
Text text1 = [[Text alloc]init];
/*假设[[Text alloc]init]生成的对象叫做对象A,则text1持有A的强引用,
对象A引用计数为1
*/
Text text2 = [[Text alloc]init];
/*假设[[Text alloc]init]生成的对象叫做对象B,则text2持有B的强引用,
对象B引用计数为1
*/
[text1 setLabel:text2];
/*text1(即对象A)的属性Label持有对text2(即对象B)的强引用
对象B引用计数为2
此时,持有对象B强引用的有Text2和text1的属性Label
*/
[text2 setLabel:text1];
/*text2(即对象B)的属性Label持有对text1(即对象A)的强引用
对象A引用计数为2
此时,持有对象A强引用的有Text1和text2的属性Label
*/
}
/*由于text1变量超出其作用域,强引用失效,即对对象A的强引用失效,对象A的引用计数为1
由于text2变量超出其作用域,强引用失效,即对对象B的强引用失效,对象B的引用计数为1
此时对象A的强引用还有text2(即对象B)的属性Label,无法释放对象A
对象B的强引用还有text1(即对象A)的属性Label,无法释放对象B
由于循环引用 发生内存泄漏
这只是一个场景,发生循环引用的场景有很多,不过实质都是两个对象间的相互引用,导致相互释放,一直占用着内存。
这时,我们刚才学习的weak便可以发挥作用了
@interface Text : NSObject
//我们把刚才的属性修饰符由strong改为weak
@property(nonatomic,weak) UILabel *label;
@end
- (UILabel *)setLabel:(UILabel *)obj
{
_label = obj;
}
{
Text text1 = [[Text alloc]init];
//假设[[Text alloc]init]生成的对象叫做对象A,则text1持有A的强引用,对象A引用计数为1
Text text2 = [[Text alloc]init];
//假设[[Text alloc]init]生成的对象叫做对象B,则text2持有B的强引用,对象B引用计数为1
[text1 setLabel:text2];
/*text1(即对象A)的属性Label持有对text2(即对象B)的弱引用,对象B引用计数为1
此时,持有对象B强引用的有Text2和text1的属性Label
*/
[text2 setLabel:text1];
/*text2(即对象B)的属性Label持有对text1(即对象A)的弱引用,对象A引用计数为1
此时,持有对象A强引用的有Text1和text2的属性Label
*/
}
/*由于text1变量超出其作用域,强引用失效,即对对象A的强引用失效,对象A的引用计数为0,对象A被释放
由于text2变量超出其作用域,强引用失效,即对对象B的强引用失效,对象B的引用计数为0,对象B被释放
没有发生循环引用,问题被解决
*/