欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

内存管理的思考方式,ARC中weak、strong的区别,循环引用

程序员文章站 2024-01-15 08:28:40
...

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被释放
    没有发生循环引用,问题被解决
    */