您的位置:首页 > 移动开发 > IOS开发

iOS 下ARC关于内存管理的引用计数问题

2016-06-21 16:28 615 查看
ARC有效时所有类型都必须加上所有权修饰符。所有权修饰符一共有四种:

__strong 修饰符
__weak 修饰符
__unsafe unretained 修饰符
__autoreleasing 修饰符
__strong 修饰符

__strong修饰符是默认修饰符,表示对象的“强引用”,强引用对象在超出其作用域时将会被废弃,引用的对象释放。

id __strong obj1 = [[NSObject alloc]init];
id __strong obj2 = obj1;
obj1 = nil;
/**
*  obj1 = nil, obj2 != nil
*
*/

UIView *view1 = [[UIView alloc]init];
UIView *view2 = view1;

view1.alpha = 0.4;
view2.alpha = 0.5;
/**
*  view1.alpha = 0.5, view2.alpha = 0.5
*
*/

通过上面这段代码希望大家能明白内存管理的思考方式。

__strong修饰符能够对同一段内存进行持有,并共同管理。

obj1释放时引用计数-1,这个时候只有obj2指向内存,因为是强引用,所以这个时候引用计数仍为1,所以内存并没有释放。

obj1和obj2的地位相同,也就是都能够对内存进行管理。

view1和view2共同管理同一段内存,所以当view2修改以后,view1的值也会进行变化,因为指向同一段内存。

__weak 修饰符

然而strong修饰符并不能解决所有问题,当两个对象相互强引用对方的成员变量的时候,就会发生循环引用,循环引用容易发生内存泄漏。所谓内存泄漏就是应当废弃的对象在超出其生命周期后继续存在。那么在这个时候就引入了weak修饰符,“弱引用”。

因为带
weak修饰符的变量(即弱引用)不持有对象,所以在超出其作用域时,对象就会释放,所以因为强引用而造成的循环引用,将其中的成员变量改为弱引用,就不会发生相同情况。

__weak修饰符还有另外一个优点。在持有某若引用时,若该对象被废弃,则此弱引用将自动失效且处于nil被赋值状态(空弱引用)。如以下代码所示。

NSObject __strong *obj1 = [[NSObject alloc]init];
NSObject __weak *obj2 = obj1;

obj1 = nil;
/**
*  obj1 = nil, obj2 = nil;
*
*/

通过使用weak修饰符可避免循环引用。通过检查附有weak修饰符的变量是否为nil,可以判断被赋值对象是否已废弃。

遗憾的是,weak修饰符只能用于iOS5以上及OS X Lion以上版本的应用程序。在iOS4以及OS X Snow Leopard的应用程序中可使用unsafe unretained修饰符来代替。

__unsafe unretained 修饰符

unsafe unretained与weak修饰符一样不会增加引用计数,自己生成的对象不能继续为自己所有,所以会立即释放。

那么
unsafe unretained修饰符与weak修饰符有什么区别呢?

比如在iOS4以及OS X Snow Leopard的应用程序中,必须使用unsafe unretained修饰符来替代weak修饰符。赋值给附有__unsafe unretained修饰符变量的对象在通过该变量使用时,如果没有确保其存在,那么应用就会崩溃。

__autoreleasing 修饰符

ARC有效时不能使用autorelease方法,同时不能使用NSAutoreleasePool类。但是,事实上ARC有效时auto lease功能也是起作用的。

以下两段代码是相同的:

/*ARC无效*/
NSAutoreleasePool *pool = [[NS NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];


[code]/*ARC有效*/
@autoreleasepool {
id __autoreleasing obj = [[NSObject alloc]init];
/**
*  obj 未释放
*
*/
}
/**
*  obj已释放
*
*/

指定“@autoreleasepool块”来替代“NSAutoreleasePool类对象生成、持有以及废弃”这一范围。

另外,ARC有效时,要通过将对象赋值给附加了autoreleasing修饰符的变量来替代调用autorelease方法。对象赋值给附有autoreleasing修饰符的变量等价于在ARC有效时调用对象的autorelease方法,即对象被注册到autoreleasepool。

也就是可以说ARC有效时,用@aotureleasepool块替代NSAutoreleasePool类,用附有__autoreleasing修饰符的变量替代autorelease方法。

因为autoreleasepool范围以块级源代码表示,提高了程序的可读性,所以今后在ARC无效时也推荐使用@autoreleaseepool块。

另外,无论ARC是否有效,调试用的非公开函数_objc_autoreleasePoolPrint()都可使用。

_objc_rootRetainCount(obj)

利用这一函数可有效的帮助我们调试注册到autoreleasepool上的对象。

上面讲解了四种修饰符,在ARC有效的情况下,必须遵守一定的规则。下面就是具体的ARC规则:

不能使用retain/release/retainCount/autorelease
不能使用NSAllocateObject/NSDeallocateObject
必须遵守内存管理的方法命名规则
不能显示的调用dealloc
使用@autoreleasepool块来替代NSAutoreleasePool
不能使用区域(NSZone)
对象型变量不能作为C语言结构体(struct/union)的成员
显示的转化“id”和“void*”

最后我们来看一下ARC中自动引用计数的数值究竟是多少

我们来看这段代码:

{
id __strong obj = [[NSObject alloc]init];   // retian count = 1;

id __weak o = obj;                          // retian count = 1;
}
//retain count = 0;


和我们预期的一样,strong修饰符使引用技术+1,而weak修饰符,并不会使修饰符+1,早超出obj的作用域以后,引用技术-1,同时释放。

我们再来看一下用__autoreleasing修饰符向autoreleasepool注册会怎么样:

@autoreleasepool {

id __strong obj = [[NSObject alloc]init];   // retian count = 1;

id __autoreleasing o = obj;                 // retian count = 2;
}
//retain count = 0;


__autoreleasing修饰符,使引用计数+1,而在超出autoreleasepool以后则清空并释放。

最后再来看一下在autoreleasepool中使用__weak修饰符是什么样的:

@autoreleasepool {

id __strong obj = [[NSObject alloc]init];   // retian count = 1;
id __weak o = obj;                 // retian count = 2;
}


在autoreleasepool中即使不使用autoreleasing修饰符,而用__weak修饰符替代,同样将obj对象注册到了autoreleasepool中。

原文链接:http://www.jianshu.com/p/dd01101666b5
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: