Objective-C内存管理(二)ARC下的内存管理

上一篇说了ARC无效时也就是MRC的内存管理规则,在ARC有效时,引用计数式内存管理的本质部分并没有改变,ARC只是自动帮助我们处理引用计数的相关部分,我们无需再键入retain 、 release 、autorelease,而是对id类型和对象类型附加所有权修饰符来记述。所有权修饰符共有四种,下面一一介绍、、、

1. __strong修饰符


①  id类型和对象类型的默认所有权修饰符。

id obj = [[NSObject alloc]init]; //该句源代码与以下这句相同
id __strong obj = [[NSObject alloc]init];

②  表示对于对象的强引用,当变量在超出其变量作用域时,强引用失效,该变量所引用的对象会被释放。

③ 对于非自己生成的对象, 使用__strong修饰符的变量也会持有该对象

NSArray __strong *arr = [NSArray array];
/*
变量arr使用__strong修饰符修饰,持有该非自己生成的对象
*/

④  __strong修饰符修饰的变量可以相互赋值

id obj0 = [[NSObject alloc]init]; //obj0持有对象A的强引用
id obj1 = [[NSObject alloc]init]; //obj1持有对象B的强引用
id obj2 = nil;       //obj2不持有任何对象
     obj2 = obj0;      //obj2持有对象A的强引用
obj1 = obj2;      //obj1持有对象A的强引用
                      //此时对象B没有持有者,引用计数为0,被废弃。
//对象A的持有者为obj0,obj1,obj2,故引用计数为3
        
 obj0 = nil;
 obj1 = nil;
 obj2 = nil;
 //此时对象A没有持有者,引用计数为0,被废弃

在使用__strong修饰符修饰的变量相互赋值时,有时会出现循环引用,也就是互相引用的问题。

先看一个互相引用的栗子

@interface Test : NSObject
{
    id _obj;
}

- (void)setObject:(id)obj;
@end

@implementation Test
- (void)setObject:(id)obj{
    _obj = obj;
}
@end

{
        Test *t1 = [[Test alloc]init];//t1持有对象A的强引用
        Test *t2 = [[Test alloc]init];//t2持有对象B的强引用
        [t1 setObject:t2];//对象A的_obj成员变量持有对象B的强引用
        [t2 setObject:t1];//对象B的_obj成员变量持有对象A的强引用
}
        /*
         t1超出作用域,强引用失效,释放对于对象A的引用
         t2超出作用域,强引用失效,释放对于对象B的引用
         此时,对象A的_obj成员变量持有对象B的强引用,对象B的_obj成员变量持有对象A的强引用
         二者互相引用,无法释放,发生内存泄漏
         */
还有一个自身强引用的栗子
Test *t1 = [[Test alloc]init];
  [t1 setObject:t1];
像这样,循环引用容易发生内存泄漏, 所谓内存泄漏指应当被废弃的对象在超出其生存期后继续存在。下面说的 __weak修饰符可以解决这个问题。


2.__weak修饰符


提供对象的弱引用,不持有对象,在超出变量作用域时,引用的对象即被释放。当持有某对象的弱引用时,该对象被废弃,则此弱引用自动失效切被赋值为nil。

id __weak obj1 = nil;
 {
       id obj0 = [[NSObject alloc]init];//obj0持有对象A的强引用
       obj1 = obj0;
       //obj0持有对象A的弱引用,也就是不持有对象A
       NSLog(@"被赋值 obj1:%@",obj1);
 }
 //变量obj0超出作用域,强引用失效,释放自己持有的对象,对象A没有持有者,被废弃
 //由于对象A被废弃,obj1的弱引用自动失效,且被赋值为nil(空弱引用)
 NSLog(@"对象A被废弃 obj1:%@",obj1);
前面已经说了,使用__weak修饰符可以解决互相引用的问题,那么对于前面说到的问题解决方法如下:

@interface Test : NSObject
{
    id __weak _obj;
}

- (void)setObject:(id)obj;
@end
__weak修饰符不持有对象,如果把自己生成并持有的对象赋值给__weak修饰的变量会怎样呢?


在此例中,由alloc方法生成的实例变量是自己生成并持有,但是__weak修饰的变量不持有对象,所以生成的对象会立即被释放,故编译器给出警告。



3.__unsafe_unretained修饰符


不安全的所有权修饰符,所修饰的变量不属于编译器的内存管理对象,既不持有对象的强引用也不持有弱引用。使用该修饰符时要确保被赋值的对象确实存在,使用该修饰符的变量在引用的对象释放后不会被赋值为nil

id __unsafe_unretained obj2 = nil;
 {
       id obj = [[NSObject alloc]init];//obj持有对象A的强引用
       obj2 = obj;//obj2对于对象A既不持有强引用也不持有弱引用
       NSLog(@"A: %@",obj2);//输出obj2变量所表示的对象
 }
  //此时,obj超出作用域,释放持有的对象,对象A没有持有者,被废弃
  NSLog(@"B: %@",obj2);
  //输出obj2所表示的对象,但是对象已经被废弃,所以是错误访问故可能在运行过程中崩溃

4.__autoreleasing修饰符


 MRC下的autorelease方法对应,使用该修饰符修饰的对象会被自动注册到autoreleasepool中。ARC下,不能使用autorelease方法,也不能使用NSAutoreleasePool类,但是可以使用@autoreleasepool{}块来代替NSAutoreleasePool的作用,使用__autorelease修饰符来代替autorelease方法。

 一般来说显式的添加__autorelease修饰就和显式的添加__strong修饰符一样罕见。几种情况说明如下:

1️⃣ 不是以allocnewcopymutableCopy开头的方法,其返回值的对象默认注册到autoreleasepool


@autoreleasepool {
            
            id obj = [NSArray array];
            //obj为强引用,所以取得非自己生成的对象并持有
            //该对象由编译器判断方法名后自动注册到autoreleasepool中
        }
        //obj超出作用域,强引用失效,自动释放所持有的对象
        //@autoreleasepool块结束,注册到autoreleasepool中的所有对象被自动释放
        //对象的所有者不存在,被废弃

 2️⃣访问__weak修饰的变量时,该对象默认注册到autoreleasepool中。因为__weak修饰符修饰的变量只持有对象的弱引用,在访问的过程中很可能该对象已经被废弃。将该对象注册到@autoreleasepool中可以保证在@autoreleasepool块结束前该对象不会被释放

 3️⃣id类型的指针或者对象类型的指针,没有显式指定时会被加上

 id *obj      <===>      id __autoreleasing *obj 

  NSObject **obj    <===>      NSObject * __autoreleasing *obj;



四个所有权修饰符就说完啦~接下来说的是ARC的具体规则


1️⃣  不能使用 retainreleaseretainCountautorelease 方法,这几种方法只能在ARC无效且手动管理内存时使用

2️⃣  不能使用NSAllocateObjectNADealoocateObjectARC下一般通过NSObject类的alloc类方法来生成并持有对象

3️⃣  遵守内存管理的方法命名规则:以alloc / new / copy / mutableCopy开头的方法必须返回给调用方所应当持有的对象;以init开始的方法必须是实例方法,且必须返回对象,类型为id类型或该方法声明类的对象类型或该类的超类型或子类型,此返回对象不注册到autoreleasepool中,只是对alloc方法返回值的对象进行初始化处理且返回该对象

4️⃣ 不能显示调用dealloc。无论ARC是否有效,只要对象没有持有者,该对象就会被废弃,调用对象的dealloc方法

5️⃣  使用@autoreleasepool块代替NSAutoreleasePool

6️⃣  不能使用区域(NSZone)。不管ARC是否有效,NSZone在现在的运行时系统中已被单纯的忽略

7️⃣  对象类型不能作为C语言结构体的成员。因为ARC由编译器管理内存,所以编译器必须要知道并管理对象的生存周期,但是C语言没有方法来管理结构体成员的生存周期,故这在标准上就是不可实现的。对象型变量要想假如结构体成员时,可以强制转换成void*或者使用__unsafe_unretained修饰符修饰。

struct Data{
    NSArray __unsafe_unretained *array;
};

8️⃣ 不能显示转换void* id类型。在ARC无效时,显式转换二者不会出现问题,但在ARC中会出现编译错误,在ARC下可以通过 __bridge 来实现相互转换。__bridge还有两种转换,分别是__bridge_retained__bridge_transfer__brigde_retained转换可以使转换赋值的变量持有所赋值的对象,__bridge_transfer使被转换的变量所持有的对象在变量被赋值给转换目标后释放。

id obj = [[NSObject alloc]init];
void *p = (__bridge void *)obj;
id o = (__bridge id)p;
        
void *p1 = (__bridge_retained void *)o;
        
id o1 = (__bridge_transfer id)p1;

最后一个要说的是,属性声明中的属性和所有权修饰符对应的关系

  属性     所有权修饰符
    assign                        __unsafe_unretaine
 copy                                __strong
   retain                               __strong
         strong                              __strong
 unsafe_unretained               __unsafe_unretained 
         weak                                __weak

总结:这一篇介绍了ARC下的所有权修饰符、具体规则、属性和所有权修饰符的对应关系。

如果对您有所帮助,请记得点赞哦~谢谢~


  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值