iOS: iOS5新特性 ARC

125 篇文章 1 订阅

apple推荐使用ARC。


iOS 5 ARC完全指南.pdf


参考文档:

Best ARC Tutorial:

http://www.raywenderlich.com/5677/beginning-arc-in-ios-5-part-1

http://www.raywenderlich.com/5773/beginning-arc-in-ios-5-tutorial-part-2


iPhone开发之深入浅出系列文章:

http://www.yifeiyang.net/development-of-the-iphone-simply-1/

http://www.yifeiyang.net/development-of-the-iphone-simply-2/

http://www.yifeiyang.net/development-of-the-iphone-simply-3/

http://www.yifeiyang.net/development-of-the-iphone-simply-4/

http://www.yifeiyang.net/development-of-the-iphone-simply/


ARC整理: 

http://www.cnblogs.com/v2m_/archive/2011/11/30/2269408.html

http://blog.csdn.net/wilmer_wu/article/details/6974383


使用ARC的知识点:

1. 简单地说,ARC就是在编译过程中在代码里自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。

2. ARC并不是GC,它只是一种代码静态分析(Static Analyzer)工具,它让编译器(LLVM 3.0)在编译代码时,自动生成实例的引用计数管理部分代码。

3. retain, release, autorelease, dealloc由编译器自动插入,不能在代码中调用

4.1 dealloc虽然可以被重载,但是不能调用[super dealloc]

4.2 不能使用NSAllocateObject, NSDeallocateObject

4.3 不能在C结构体中使用对象指针

4.4 id与void *间的如果cast时需要用特定的方法(__bridge关键字)

4.5 不能使用NSAutoReleasePool、而需要@autoreleasepool块

4.6 不能使用“new”开始的属性名称

把@property(assign)变为@property(weak)

5. ARC处理对象的规则是:只要对象的持有者存在(对象被强参照:strong),那么就可以使用该对象; 对象失去了持有者后,即被破弃 (详见 http://www.yifeiyang.net/development-of-the-iphone-simply-1/)

5.1 如果不是 alloc/new/copy/mutableCopy 开头的函数,编译器会将生成的对象自动放入 autoReleasePool 中。如果是 __strong 修饰的变量,编译器会自动给其加上所有权。

6. 弱参照 (Weak reference) 使用关键字__weak来声明弱参照weakName变量,弱参照只使用于iOS5.0 or later。该类型的变量不是对象的持有者,所以不影响对象的生命周期。所以如果对象没有持有者,对象就会被抛弃,那么对应的弱参照变量就会变为nil

7. ARC中关于对象的引用参照,主要有下面几关键字。

*__strong变量声明缺省都带有__strong关键字,如果变量什么关键字都不写,那么缺省就是强参照

* __weak: 弱参照类型。该类型的变量不是对象的持有者,所以不影响对象的生命周期。所以如果对象没有持有者,对象就会被抛弃,那么对应的弱参照变量就会变为nil

循环参照 的问题。循环参照指的是两个对象被互相强参照,以至于任一对象都不能释放

当多个对象间有“父子关系”时,需要在一侧用“弱参照”来解决循环参照问题。一般情况下,“父亲”作为“孩子”的拥有者,对“孩子”是强参照,而“孩子”对父亲是弱参照

OS程序中经常用到delegate模式,比如ViewController中,用ModalView打开/关闭DetailViewController时,需要delegate的设定。使用弱参照来声明

通常outlet都是使用弱参照来声明,除了对StoryBoard Segue这样nib中间中的顶层对象,就要用strong了

* __unsafe_unretained与__weak一样,也是弱参照,与__weak的区别只是是否执行nil赋值(Zeroing)。但是这样,需要注意变量所指的对象已经被破弃了,地址还还存在,但内存中对象已经没有了。如果还是访问该对象,将引起「BAD_ACCESS」错误

该关键字使对像延迟释放。比如你想传一个未初始化的对像引用到一个方法当中,在此方法中实例化此对像,那么这种情况可以使用__autoreleasing。他被经常用于函数有值参数返回时的处理。

- (void) generateErrorInVariable:(__autoreleasing NSError **)paramError {
    ....
    *paramError = [[NSError alloc] initWithDomain:@"MyApp" code:1 userInfo:errorDictionary];
}

....
{
    NSError *error = nil;
    [self generateErrorInVariable:&error];
    NSLog(@"Error = %@", error);
}

又比如函数的返回值是在函数中申请的,那么希望释放是在调用端时,应该使用__autoreleasing

-(NSString *)stringTest
{
    __autoreleasing NSString *retStr = [NSString alloc] initWithString:@"test"];

    return retStr;
}


ARC的使用规则:

__strong

我们先来看看用 __strong 修饰的变量,以及缺省隐藏的 __strong 情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
{
    /*
     * 生成对象并拥有所有权
     */
    id __strong obj = [[NSObject alloc] init];

    /*
     * 自己一直是持有对象状态
     */
}
    /*
     * 变量出生命周期时(即在"{}"外,失去全部所有者,对象内存空间被释放
     */

这种情况毫无悬念,缺省使用 alloc/new/copy/mutableCopy 开头的函数也是这样的结果。并且在这里,编译器帮我们自动的调用了对象的 release 函数,不需要手工维护。再看看下面的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
    /*
     * 生成对象但是并没有其所有权,即是持有者,但不是所有者
     */
    id __strong obj = [NSMutableArray array];

    /*
     * 由于变量声明是强引用,自己一直是持有对象状态
     * 编译器根据函数名,再将该对象放入 autoreleasepool 中
     */
}
    /*
     * 变量出生命周期时
(即在"{}"外)
,失去全部所有者,对象内存空间被释放 */

由上,虽然不是用 alloc/new/copy/mutableCopy 开头的函数得到的对象,由于是强参照,我们任然成为对象的持有者。而这,正是编译器帮我们做到的。

具体做的是什么呢?其实就是【对象在函数中返回时,使用return [[object retain] autorelease]】所描述的;如果你反汇编一下ARC生成的代码,可以看到这时会自动调用名为 objc_retainAutoreleaseReturnValue 的函数,而其作用和 [[object retain] autorelease] 一致。编译器通过函数名分析,如果不是 alloc/new/copy/mutableCopy 开头的函数,自动加入了这段代码。

另外,缺省 __strong 修饰的变量,对象代入的时候也正确地保证对象所有者规则;代入新对象时,自动释放旧对象的参照,代入nil的时候,表示释放当前对象的强参照。

__weak

虽然大部分场合,大部分问题使用 __strong 来编码就足够了;但是为了解决循环参照的问题 __weak 关键字修饰【弱参照】变量就发挥了左右。关于循环参照的问题,准备在以后的博文中介绍;今天,主要看看编译器在背后怎么处理 __weak 变量的。

__weak 声明的变量其实是被放入一个weak表中,该表和引用计数的表格类似,是一个Hash表,都是以对象的内存地址做key,同时,针对一个对象地址的key,可以同时对应多个变量的地址。

当一个 __weak 所指对象被释放时,系统按下面步骤来处理

  • 从weak表中,通过对象地址(key)找到entry
  • 将entry中所有指向该对象的变量设为nil
  • 从weak表中删除该entry
  • 从对象引用计数表中删除对象entry(通过通过对象地址找到)

另外,当使用 __weak 修饰的变量的时候,变量将放入 autoreleasepool 中,并且用几次放几次。比如下面的简单例子。

1
2
3
4
5
6
7
8
{
    id __weak o = obj;
    NSLog(@"1 %@", o);
    NSLog(@"2 %@", o);
    NSLog(@"3 %@", o);
    NSLog(@"4 %@", o);
    NSLog(@"5 %@", o);
}

这里我们用了5次,那么pool中就被登录了5次;从效率上考虑这样当然不是很好,可以通过代入 __strong 修饰的强参照变量来避开这个问题。

1
2
3
4
5
6
7
8
9
{
    id __weak o = obj;
    id temp = o;
    NSLog(@"1 %@", temp);
    NSLog(@"2 %@", temp);
    NSLog(@"3 %@", temp);
    NSLog(@"4 %@", temp);
    NSLog(@"5 %@", temp);
}

另外,还有通过重载 allowsWeakReference/retainWeakReference 函数来限制 __weak 声明变量使用回数的方法,毕竟不在本次讨论范畴之内,就此省略。

话说回来,为什么使用弱参照变量的时候,要将其放入 autoreleasepool 中呢?想想弱参照的定义就应该明白了 —- 如果在访问弱参照对象时,该对象被释放了怎么办,程序不就崩溃了嘛;所以为了解决该问题,又再一次用到了 pool。

__autoreleasing

虽然上面还没有降到该关键字,但是编译器在很多时候已经用到了 autoreleasepool。比如非 alloc/new/copy/mutableCopy 开头的函数返回一个对象的时候,又比如使用一个 __weak 声明的变量的时候。

实际上,写ARC代码的时候,明示 __autoreleasing 声明变量和明示 __strong 声明变量一样基本上没有,因为编译器已经为我们做了很多,很智能了(前提是我们要按ARC的规则写代码)。

还有一种编译器缺省使用 __autoreleasing 关键字声明变量的时候:对象指针类型。比如下面的对应关系。

1
2
   id *obj == id __autoreleasing *obj
   NSObject **obj == NSObject * __autoreleasing *obj

所以,下面两个函数的是等价的。

1
2
3
-(BOOL)performOperationWithError:(NSError **)error;

-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;

像下面的函数调用,为什么是可行的呢?

1
2
NSError __strong *error = nil;
BOOL result = [obj performOperationWithError:&error];

其实,编译器是这样解释这段代码的。

1
2
3
4
NSError __strong *error = nil;
NSError __autoreleasing *tmp = error;
BOOL result = [obj performOperationWithError:&tmp];
error = tmp;

那么我们这样声明函数不就可以了吗?

1
-(BOOL)performOperationWithError:(NSError * __strong *)error;

答案是肯定的,你可以这样做,编译是可以通过,但你违反了非 alloc/new/copy/mutableCopy 开头的函数,不返回对象持有权的原则。这里是没有问题了,但也许影响到其他地方NG


ARC对@property的使用规则有何影响。

我们先来看看与所有权有关系的属性,关键字间的对应关系。

属性值 关键字 所有权
strong __strong
weak __weak
unsafe_unretained __unsafe_unretained
copy __strong
assign __unsafe_unretained
retain __strong
strong

该属性值对应 __strong 关键字,即该属性所声明的变量将成为对象的持有者。

weak

该属性对应 __weak 关键字,与 __weak 定义的变量一致,该属性所声明的变量将没有对象的所有权,并且当对象被破弃之后,对象将被自动赋值nil。

并且,delegate 和 Outlet 应该用 weak 属性来声明。同时,如上一回介绍的iOS 5 之前的版本是没有 __weak 关键字的,所以 weak 属性是不能使用的。这种情况我们使用 unsafe_unretained。

unsafe_unretained

等效于__unsafe_unretaind关键字声明的变量;像上面说明的,iOS 5之前的系统用该属性代替 weak 来使用。

copy

与 strong 的区别是声明变量是拷贝对象的持有者。

assign

一般Scalar Varible用该属性声明,比如,int, BOOL。Scalar Varible的变量缺省都有 assign 的属性定义,所以不需要给他们单独的明示声明了

retain

该属性与 strong 一致;只是可读性更强一些


ARC 产生之前的 Objective-C 内存手动管理世界是怎样的:

http://www.yifeiyang.net/development-of-the-iphone-simply-3/

简单来说,Objective-C 中内存管理的方式其实就是指 引用计数 (Reference Counting)的使用准则。对象生成的时候必定被某个持有者拿着,如果有多个持有者的话,其引用计数就会递增;相反失去一个持有者那么引用计数即会递减,直到失去所有的持有者,才真正地从内测中释放自己。


内存管理的依循下面的基本原则

  • 自己生成的对象,那么既是其持有者
  • 不是自己生成的对象,也可成为其持有者(一个对象可以被多个人持有)
  • 如果不想持有对象的时候,必须释放其所有权
  • 不能释放已不再持有所有权的对象

结合 Objective-C 语言中的方法,我们来看看基本的内存管理。

方法 动作
alloc/new/copy/mutableCopy 生成对象并拥有所有权
retain 拥有对象所有权
release 释放对象所有权
dealloc 释放对象资源

实际上这些函数并不能说是 Objective-C 语言所特有的,而是 OS X / iOS 系统库中包含的基类函数;具体说就是 Cocoa Framework::Foundation::NSObject 基类的成员函数。

Objective-C 语言内部严格遵守上面表格中的定义;首先是 alloc/new/copy/mutableCopy 这几个函数,并且是alloc/new/copy/mutableCopy 开头的函数,比如:allpcMyObject/newTheObject/copyThis/mutableCopyTheObject 等都必须遵循这个"生成对象并拥有所有权"的原则。

如下面的例子是一个alloc开头的函数,alloc 生成的对象,其所有权会传递给函数的调用端;即满足了 alloc 开头函数的命名规则

-(id)allocObject {
    /*
     * 生成对象并拥有所有权
     */
    id obj = [[NSObject alloc] init];

    /*
     * 自己一直是持有对象状态
     */
    return obj;
}

调用    id *obj=[xxx allocOjbect]   后要必须必须手动release来释放obj变量。


反而言之,如果不是 alloc/new/copy/mutableCopy 开头的函数,而且要返回对象的话,那么调用端只是生成对象,而不是其持有者

-(id)object {
    id obj = [[NSObject alloc] init];

    /*
     * 自己一直是持有对象状态
     */

    [obj autorelease];

    /*
     * 对象还存在,只是并不持有它的所有权
     */

    return obj;
}

该例子中,调用    id *obj=[xxx allocOjbect]   后,内存管理上是autorelease的,不用手动显式release obj


上面2个例子的真实例子是:NSString的initWithFormat 和stringWithFormat.

1、initWithFormat是实例方法

只能通过 NSString* str = [[NSString alloc] initWithFormat:@"%@",@"Hello World"] 调用,但是必须手动release来释放内存资源

2、stringWithFormat是类方法

可以直接用 NSString* str = [NSString stringWithFormat:@"%@",@"Hello World"] 调用,内存管理上是autorelease的,不用手动显式release



在 ARC 诞生之前,往往用下面准则来写代码 (详见: http://www.yifeiyang.net/development-of-the-iphone-simply-3/)

* 生成对象时,使用autorelease
* 对象代入时,先autorelease后再retain
* 对象在函数中返回时,使用return [[object retain] autorelease]







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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值