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

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];

  /*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中。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值