分享

理解 Objective

 wintelsui 2018-04-02

 自动引用计数(Automatic Reference Counting, ARC)把压在程序员们肩头的管理内存的重担卸除了不少,更不用说让跟踪内存泄漏那样的烦心事也少了很多。不过,虽然ARC很棒,我们仍然不能完全把内存管理这回事儿抛在脑后。

这篇文章将要讨论以下方面的问题,帮助大家快速进入ARC的世界。

  1. 内存的引用计数: 快速复习
  2. ARC的工作原理
  3. 在工程中开启ARC
  4. ARC施加的新规则
  5. ARC限定符 - 声明的属性
  6. ARC限定符 - 常规变量
  7. 移植到ARC
  8. 引入不兼容ARC的代码
  9. 我该用ARC吗?
AlfredCheung
AlfredCheung
翻译于 5年前
3人顶
 翻译得不错哦!

发生了什么事?

ARC Related changes to Xcode 4.2

在ARC出现以前,程序员们只能靠retain/relese/autorelease来确保对象们恰好“坚持”到被需要的那一刻。如果忘了retain,或者多次release某个对象,程序就会发生内存泄漏的问题,甚至直接崩溃。

在Xcode 4.2中,除了语法检查外,Apple的新LLVM编译器还将内存管理的苦差事接了过来,它会检查代码,决定何时释放对象。Apple的文档里是这么定义ARC的:

“自动引用计数(ARC)是一个编译器级的功能,它能简化Cocoa应用中对象生命周期管理(内存管理)的流程。”

ARC使内存管理在大部分时候变得如同小事一桩,但我们仍要在决定自己的类如何管理其它对象的引用时承担一些责任。

那么,让我们正式开始吧……

AlfredCheung
AlfredCheung
翻译于 5年前
2人顶
 翻译得不错哦!

引用计数: 快速复习

手工管理、引用计数式的内存管理在iOS中是这样工作的: 当使用alloc/init(或其它类似方法)创建对象时,随同对象返回的,还有个retainCount,其值为1,表明我们获得了这个对象的所有权。

NSObject *obj = [[NSObject alloc] init];
// do some stuff
[obj release];
在对象的alloc/init和release(即放弃对象的所有权)之间,我们可以任意处理它,这很安全,因为系统是无法回收正在使用中的对象的。

将对象加入到自动释放池也是类似,对象会一直存在,直到未来的某个时间我们不再需要它,才会被系统回收。

-(NSObject*) someMethod {
  NSObject *obj = [[[NSObject alloc] init] autorelease];
  return obj; // will be deallocated by autorelease pool later
}
AlfredCheung
AlfredCheung
翻译于 5年前
2人顶
 翻译得不错哦!

ARC的工作原理

大多数新的iOS程序员都会在引用计数这问题上遇到理解障碍。ARC则是一个编译前的步骤,它为我们的代码自动加上retain/release/autorelease语句。

ARC并不是垃圾收集,而且,引用计数也没有消失,只是变成自动而已。听起来像是事后追加的这么一个功能,不过,只要我们想一想Objective-C有多少功能是通过对源文件的预处理来实现的,就不会这么想了。

当采用ARC后,代码只要这样写:

NSObject *obj = [[NSObject alloc] init];
// do some stuff
ARC会自动将它变成:
NSObject *obj = [[NSObject alloc] init];
// do some stuff
[obj release]; // **Added by ARC**

从下图(来自Apple官方文档)看起来,好像retain/release的数量快赶上真正有用的代码了。当然,这肯定不是熟手的情况,不过可以看成是对新手的保守估计。这些代码跑起来,要跟踪某个内存问题真的是会搞死人。

来源: Programming With ARC Release Notes

AlfredCheung
AlfredCheung
翻译于 5年前
1人顶
 翻译得不错哦!

在工程中开启ARC

如果想开启ARC,只要在工程的Build Settings中设置ARC为YES。在幕后,实际上是设置了-fobj-arc的编译器标识。


ARC施加的新规则

如果想用ARC,必须服从一些新规则。

1. 对象的Alloc/Init

创建对象的方法跟以前一样,但你一定不能调用retain/release/autorelease/retainCount。也不能通过selector偷偷地调用它们: 禁止使用@selector(retain)和@selector(release)。

AlfredCheung
AlfredCheung
翻译于 5年前
2人顶
 翻译得不错哦!

2. dealloc方法

ARC为自动为你调用,一定不能直接调用dealloc。不过,如果你需要释放实例变量以外的资源,还是可以创建自定义的dealloc方法。但在这个方法里,不要调用[super dealloc]。因为ARC会帮你调。

3. 声明的属性

在ARC之前,我们是用@property指令中的assign/retain/copy参数来告诉编译器,如何管理这些属性的内存。用了ARC之后,这些参数就作废了,改用weak/strong这两个参数。

AlfredCheung
AlfredCheung
翻译于 5年前
1人顶
 翻译得不错哦!

4. C结构中的对象指针

同样禁止使用。文档里建议不要把它们放在结构了,改放到类里去。否则ARC就不认识它们了。可能会出现一些移植上的问题。不过,ARC是可以以文件为单位来关闭的。参考下文的“引入不兼容ARC的代码”。

5. id与void*之间的临时互转

当我们在Core Foundation的C函数和Foundation Kit的Objective-C方法间传递对象时,常常需要进行id和void*两个类型的互转。叫做免费桥接(Toll Free Bridging)

如果使用ARC,必须在CF对象进入和脱离ARC的控制时,用提示/限定符来告知编译器。限定符有__bridge、__bridge_retain和__bridge_transfer。另外,仍需要用CFRetain和CFRelease来管理Core Foundation的对象。

这一块已经比较高深了,如果你不清楚CF对象是什么,也不需要太烦恼。

AlfredCheung
AlfredCheung
翻译于 5年前
1人顶
 翻译得不错哦!

6. 以@autoreleasepool代替NSAutoReleasePool

兼容ARC的代码不能再使用NSAutoReleasePool对象,而要改用@autoreleasepool{}块。一个很好的例子:

int main(int argc, char *argv[])
{
  @autoreleasepool {
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([ExampleAppDelegate class]));
  }
}

7. 其它

基于Zone的内存已经没了(在运行时里也没了)。不能再使用NSAllocateObject和NSDeallocateObject。

AlfredCheung
AlfredCheung
翻译于 5年前
1人顶
 翻译得不错哦!

ARC限定符 - 声明的属性

身为程序员,习惯于做出一些决定,例如把某个量声明为变量还是常量、本地还是全局,等等。因此,在这里,我们也要决定某个属性与其它属性的关系。我们用strong/weak来把这一关系告诉编译器。

强引用

强引用是对某对象的引用,并且能阻止它被回收。换句话说,强引用创建了一个所有关系。在ARC之前,我们这么写:

// Non-ARC Compliant Declaration
@property(retain) NSObject *obj;

在ARC下,我们需要这么写,以确保当前实例获得被引用对象的所有权(主人不被回收,它也不能被回收)。

// ARC Compliant Declaration
@property(strong) NSObject *obj;

弱引用

弱引用是对某对象的引用,但不能阻止它被回收。换句话说,弱引用并不会创建所有关系。在ARC之前,我们这么写:


// Non-ARC Compliant Declaration
@property(assign) NSObject *parentObj;
在ARC下,我们需要这么写,以确保当前实例没有获得被引用对象的所有权(一般来说,子对象不应该拥有父对象,这时可以用弱引用)。
// ARC Compliant Declaration
@property(weak) NSObject *parentObj;
AlfredCheung
AlfredCheung
翻译于 5年前
1人顶
 翻译得不错哦!

ARC限定符 - 常规变量

上一节是说明如何管理属性。对于常规变量,则有:

__strong
__weak
__unsafe_unretained
__autoreleasing
一般来说,我们不太需要使用上面这些限定符。在使用移植工具的时候可能会看到那么几个,但新工程基本上不需要。
  • __strong: 默认限定符,不需要显式指定。表示任何用alloc/init创建的对象在当前范围的生命期内得以保留。“当前范围”是指变量声明语句所在的两个大括号之间(方法、循环、块,等等)。
  • __weak: 表示对象可以随时被摧毁。只有当它被其它对象强引用时才有用。__weak变量在摧毁时,被设为nil。
  • __unsafe_unretained: 与__weak类似,但在摧毁时,不设为nil,保留原值(不再指向有效的东西)。
  • __autoreleasing: 不要与autorelease搞混,它用于通过引用传递对象,比如,通过引用传递NSError对象: [myObject performOperationWithError:&tmp]

来源http://clang./docs/AutomaticReferenceCounting.html#ownership

: 我们发现在ARC下,@property中使用“retain”时(而不是“strong”),编译器并不会报错,而且能生成同样结果。但以后可能会变,所以还是用“strong”吧。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多