iOS内存管理-所有权修饰符:__strong, __weak/__unsafe_unretained, __autoreleasing

iOS内存管理的思考方式:

(1)自己生成的对象,自己持有。

这里的“自己”固然指“对象的使用环境”,但将其理解为编程人员“自身”也是没错的。

使用由以下名称开头的方法意味着自己生成的对象只有自己持有(这些对象不会被注册到Auto Release Pool中):alloc,new,copy,mutableCopy。

eg:

    <span style="white-space:pre">	</span>id obj = [Objective new];
    <span style="white-space:pre">	</span>/*自己持有了obj对象*/


(2)非自己生成的对象,自己也能持有。

这是针对于通过(1)中那四个以外的方法来获得的对象。

eg:

<span style="font-size:10px;">	{
		id __strong obj = [NSArray array];
		/*自己持有了obj对象*/
	}
	/*出了作用域,强引用失效,所以自动地释放持有的对象*/</span>


注:“引用”是指定义的变量引用(或持有)了通过分配内存生成的对象。


(3)自己持有的对象不再需要时(由自己)释放。

(4)非自己持有的对象无法(由自己)释放。


对象的所有权


所有权对应于持有的说法。


1.__strong修饰符

strong 表示“强引用”,是默认的修饰符。strong修饰的变量在出了其作用域之后强引用失效,或者叫被废弃(即不再持有),ARC有效时,被废弃的对象会在适当的时候被系统释放,ARC无效时,若在变量作用域内不对其进行手动释放,则造成内存泄露。

一个变量本来强引用了一个对象A,若再对此变量赋值另一个对象B,则此变量不再持有A。一个对象被多个变量强引用,每把一个变量置为nil时,对象的持有者就减少一个,引用计数减1。


2.__weak/__unsafe_unretained修饰符

__strong修饰的变量互相引用或自身强引用时,会造成内存泄露,__weak修饰符正是为了解决这一问题而出现的,它避免了循环引用。

低版本系统中,__weak可用__unsafe_unretained替代。后者正如其名,是不安全的所有权修饰符。ARC是编译器的工作,但__unsafe_unretained修饰的变量不属于编译器的管理范围。结果就是,它同__weak一样,自己生成的对象不能继续被自己持有,所以生成的对象会立即被释放。

eg1:

id __weak obj = [[NSObject alloc] init];
NSLog(@"obj:%@", obj);    //输出obj为null

eg2:

id __unsafe_unretained obj1 = nil;
{
 id __strong obj0 = [[NSObject alloc] init];
obj1 = obj0;
}
/*
到这里obj0超出作用域被释放,obj1被__unsafe_unretained修饰,不能持有对象,故obj变成悬垂指针(指向已经被释放的内存)。
*/

__weak在实现中使用了一个weak表,是一种hash表结构,每个对象的地址作为表的查询键值,通过每个键值索引的是对应地址存储的对象的引用变量。对weak表的操作会消耗CPU资源,良策是只在需要避免循环引用时使用__weak修饰符。

__weak修饰的变量被废弃之后,变量会被赋值nil,其他修饰符无此功能。

__weak不增加引用计数,但在Auto Release Pool中会增加。


3.__autoreleasing修饰符

编译器会检查分配对象的方法名是否以alloc / new / copy / mutableCopy开头,这些方法是自己生成的对象自己持有,鼓不用注册到Auto Release Pool。不是则将返回的对象注册到Auto Release Pool中。

实际中一般不显式使用此修饰符,而是通过@sutoreleasepool来间接使用,当pool销毁时,会向注册到其中的所有对象发送release消息,在下一个runloop开始时会将引用计数为0的对象销毁。

隐式使用__autoreleasing的情况:

1)访问__weak修饰的变量时,实际上必定要访问注册到Auto Release Pool中的对象。这是为了避免__weak修饰的变量被立即释放(即保证在@autoreleasepool块结束之前不释放)。所以

<span style="font-size:10px;">id __weak obj1 = obj0;
NSLog(@"class=%@",[obj1 class]);</span>
与下面的代码相同:

<span style="font-size:10px;">id __weak obj1 = obj0;
id __autoreleaseing tmp = obj1;
NSLog(@"class=%@",[tmp class]);</span>

2)返回autoreleasing对象。如,以下代码是合法的:

<span style="font-size:10px;">+(id)array
{
	return [[NSMutableArray alloc] init];
}</span>
因为返回的对象被注册到了Auto Release Pool中,故不会在函数返回之后释放。

3)指针类型

id *obj;等价于 id __autoreleasing *obj;

对象的指针NSObject **obj;等价于 NSObject *__autoreleasing *obj;

因此,以下代码会产生编译错误;

<span style="font-size:10px;">NSError *err = nil;
NSError **pErr = &err;</span>

因为赋值给对象指针式,对象所有权必须一致。下面的写法就会正常编译:

<span style="font-size:10px;">NSError *err = nil;
NSError *__strong *pErr = &err;</span>

当显示使用__autoreleasing时,不能与static一起使用。


ARC规则


对象不能作为C语言结构体的成员。因为ARC把内存管理工作分配给编译器,编译器必须要知道对象的生命周期,而把对象作为C语言结构体的成员,这在C标准上就是不能实现的,因为C语言主要靠变量作用域管理其生命周期,这与ARC机制无可比性。

所有权转换:非ARC环境下,id与void*可直接转换,ARC环境下,使用bridge转换。转换效果:__bridge与__unsafe_unretained类似,__bridge_retained与retain类似。__bridge_transfer与release类似,


属性与所有权修饰符的对应关系


只有copy不是简单的赋值,而是通过copyWithZone:方法复制了源对象,再将副本赋给新值。


引用计数

所有权修饰符的作用就是通过对引用计数值的影响来实现的。一次强引用会增加引用计数,变量被注册到Auto Release Pool也会增加引用计数值(即使变量被__weak修饰)。

取得引用计数的值不能用retain方法,这个方法不是给开发者使用的。可以使用_objc_rootRetainCount(id obj)函数。但实际上,这个方法获取的值也不能完全信任,比如,对于已释放的对象、不正确的对象地址,以及在多线程编程中的竞态条件问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值