Objective-C内存管理(二)ARC下的内存管理
2018-01-26 21:56
405 查看
上一篇说了ARC无效时也就是MRC的内存管理规则,在ARC有效时,引用计数式内存管理的本质部分并没有改变,ARC只是自动帮助我们处理引用计数的相关部分,我们无需再键入retain 、 release 、autorelease,而是对id类型和对象类型附加所有权修饰符来记述。所有权修饰符共有四种,下面一一介绍、、、
1. __strong修饰符
① id类型和对象类型的默认所有权修饰符。
② 表示对于对象的强引用,当变量在超出其变量作用域时,强引用失效,该变量所引用的对象会被释放。
③ 对于非自己生成的对象, 使用__strong修饰符的变量也会持有该对象
④ __strong修饰符修饰的变量可以相互赋值
在使用__strong修饰符修饰的变量相互赋值时,有时会出现循环引用,也就是互相引用的问题。
先看一个互相引用的栗子
还有一个自身强引用的栗子
像这样,循环引用容易发生内存泄漏, 所谓内存泄漏指应当被废弃的对象在超出其生存期后继续存在。下面说的__weak修饰符可以解决这个问题。
2.__weak修饰符
提供对象的弱引用,不持有对象,在超出变量作用域时,引用的对象即被释放。当持有某对象的弱引用时,该对象被废弃,则此弱引用自动失效切被赋值为nil。
前面已经说了,使用__weak修饰符可以解决互相引用的问题,那么对于前面说到的问题解决方法如下:
__weak修饰符不持有对象,如果把自己生成并持有的对象赋值给__weak修饰的变量会怎样呢?
在此例中,由alloc方法生成的实例变量是自己生成并持有,但是__weak修饰的变量不持有对象,所以生成的对象会立即被释放,故编译器给出警告。
3.__unsafe_unretained修饰符
不安全的所有权修饰符,所修饰的变量不属于编译器的内存管理对象,既不持有对象的强引用也不持有弱引用。使用该修饰符时要确保被赋值的对象确实存在,使用该修饰符的变量在引用的对象释放后不会被赋值为nil。
4.__autoreleasing修饰符
与MRC下的autorelease方法对应,使用该修饰符修饰的对象会被自动注册到autoreleasepool中。在ARC下,不能使用autorelease方法,也不能使用NSAutoreleasePool类,但是可以使用@autoreleasepool{}块来代替NSAutoreleasePool的作用,使用__autorelease修饰符来代替autorelease方法。
一般来说显式的添加__autorelease修饰就和显式的添加__strong修饰符一样罕见。几种情况说明如下:
1️⃣ 不是以alloc、new、copy、mutableCopy开头的方法,其返回值的对象默认注册到autoreleasepool中
2️⃣访问__weak修饰的变量时,该对象默认注册到autoreleasepool中。因为__weak修饰符修饰的变量只持有对象的弱引用,在访问的过程中很可能该对象已经被废弃。将该对象注册到@autoreleasepool中可以保证在@autoreleasepool块结束前该对象不会被释放
3️⃣id类型的指针或者对象类型的指针,没有显式指定时会被加上
id *obj <===>
id __autoreleasing *obj
NSObject **obj <===> NSObject * __autoreleasing *obj;
四个所有权修饰符就说完啦~接下来说的是ARC的具体规则
1️⃣ 不能使用 retain、release、retainCount、autorelease 方法,这几种方法只能在ARC无效且手动管理内存时使用
2️⃣ 不能使用NSAllocateObject、NADealoocateObject,ARC下一般通过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修饰符修饰。
8️⃣ 不能显示转换void*和 id类型。在ARC无效时,显式转换二者不会出现问题,但在ARC中会出现编译错误,在ARC下可以通过
__bridge 来实现相互转换。__bridge还有两种转换,分别是__bridge_retained和__bridge_transfer。__brigde_retained转换可以使转换赋值的变量持有所赋值的对象,__bridge_transfer使被转换的变量所持有的对象在变量被赋值给转换目标后释放。
最后一个要说的是,属性声明中的属性和所有权修饰符对应的关系
属性
所有权修饰符
assign __unsafe_unretaine
copy
__strong
retain __strong
strong __strong
unsafe_unretained __unsafe_unretained
weak __weak
总结:这一篇介绍了ARC下的所有权修饰符、具体规则、属性和所有权修饰符的对应关系。
如果对您有所帮助,请记得点赞哦~谢谢~
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️⃣ 不是以alloc、new、copy、mutableCopy开头的方法,其返回值的对象默认注册到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️⃣ 不能使用 retain、release、retainCount、autorelease 方法,这几种方法只能在ARC无效且手动管理内存时使用
2️⃣ 不能使用NSAllocateObject、NADealoocateObject,ARC下一般通过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下的所有权修饰符、具体规则、属性和所有权修饰符的对应关系。
如果对您有所帮助,请记得点赞哦~谢谢~
相关文章推荐
- Objective-c的内存管理MRC与ARC
- Objective-C(十六、内存管理,自动释放池,ARC,强指针,弱指针,方法族)——iOS开发基础
- 第五章 通过 Objective-C 的 ARC 内存管理
- Objective-c的内存管理MRC与ARC
- objective-c启用ARC时的内存管理
- Objective-C内存管理学习之ARC规则
- Objective-C内存管理(一)ARC无效时的内存管理
- Objective-c的MRC和ARC的内存管理机制
- objective-c启用ARC时的内存管理
- 黑马程序员——Objective-C的内存管理MRC与ARC——黑马 IOS 技术博客
- objective-c启用ARC时的内存管理 (循环引用)
- 黑 马 程 序 员_视频学习总结<Objective-C>----04 内存管理、protocol、block、ARC
- Objective-C(9)内存管理之ARC
- objective-c内存管理ARC关键字概述
- Objective-C内存管理详解——ARC和垃圾回收机制
- OC内存管理教程之ARC(一)——objective-c内存管理和引用计数
- Objective-C内存管理第六弹:ARC
- Objective-C 内存管理之 _ARC
- objective-c内存管理ARC关键字概述
- Objective-c的内存管理MRC与ARC