内存管理的思考方式,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被释放
    没有发生循环引用,问题被解决
    */



猜你喜欢

转载自blog.csdn.net/qq_39551390/article/details/81325933