Objective-C中的自动引用计数不能防止或减少什么样的泄漏?

在Mac和iOS平台上,内存泄漏通常是由未释放的指针引起的。传统上,检查您的分配、副本和保留以确保每个都有相应的发布消息一直是极其重要的。

Xcode 4.2附带的工具链在最新版本的LLVM编译器中引入了自动引用计数(ARC),通过让编译器为你内存管理你的东西,完全消除了这个问题。这非常酷,它确实减少了许多不必要的、平凡的开发时间,并防止了许多不小心的内存泄漏,这些泄漏很容易通过适当的保留/释放平衡来修复。当你为Mac和iOS应用启用ARC时,即使自动释放池也需要以不同的方式管理(因为你不应该再分配自己的__abc)。

但是防止了什么其他内存泄漏,我仍然需要注意?

另外,Mac OS X和iOS上的ARC和Mac OS X上的垃圾收集有什么不同?

42281 次浏览

您仍然需要注意的与内存相关的主要问题是保留周期。当一个对象有指向另一个对象的强指针,而目标对象有指向原始对象的强指针时,就会发生这种情况。即使删除了对这些对象的所有其他引用,它们仍然会彼此保留,不会被释放。这也可以间接发生,通过一个对象链,链中的最后一个对象可能引用回一个较早的对象。

正是由于这个原因,__unsafe_unretained__weak所有权限定符才存在。前者不会保留它所指向的任何对象,但保留了该对象消失并指向坏内存的可能性,而后者不保留对象,并在其目标被释放时自动将自己设置为nil。在这两者中,__weak在支持它的平台上通常是首选的。

您可以将这些限定符用于委托之类的事情,其中您不希望对象保留其委托并可能导致循环。

另两个与内存相关的重要问题是Core Foundation对象的处理和使用malloc()char*等类型分配的内存。ARC不管理这些类型,只管理Objective-C对象,所以你仍然需要自己处理它们。Core Foundation类型可能特别棘手,因为有时它们需要桥接到匹配的Objective-C对象,反之亦然。这意味着在CF类型和Objective-C之间桥接时,控制需要从ARC来回转移。一些与此桥接相关的关键字已被添加,Mike Ash在他冗长的ARC记录中对各种桥接情况有很好的描述。

除此之外,还有其他一些不太常见但仍有潜在问题的情况,出版规范将详细介绍这些情况。

很多新的行为都是基于只要有强指针指向对象,就会保留对象,这与Mac上的垃圾收集非常相似。然而,技术基础非常不同。这种风格的内存管理依赖于我们在Objective-C中都需要遵守的严格的保留/释放规则,而不是有一个定期运行的垃圾收集器进程来清理不再被指向的对象。

ARC只是把我们多年来不得不做的重复内存管理任务卸载给编译器,这样我们就再也不用担心它们了。这样,您就不会遇到在垃圾收集平台上遇到的停止问题或锯齿状内存配置文件。我在我的垃圾收集Mac应用程序中经历过这两种情况,我渴望看到它们在ARC下的表现。

有关垃圾收集与ARC的更多信息,请参阅这是Chris Lattner在Objective-C邮件列表上的有趣回复,在那里他列出了ARC相对于Objective-C 2.0垃圾收集的许多优点。我遇到过他描述的几个GC问题。

ARC对非objc内存没有帮助,例如,如果你malloc()某个东西,你仍然需要free()它。

如果编译器不能找出选择器是什么,ARC可以被performSelector:欺骗(编译器将对此生成警告)。

ARC也会生成遵循ObjC命名约定的代码,所以如果你把ARC和MRC代码混合在一起,如果MRC代码没有做编译器认为名称承诺的事情,你会得到令人惊讶的结果。

ARC也不会管理CoreFoundation类型。你可以“桥接”它们(使用CFBridgingRelease()),但前提是你要把它用作Objective-C/Cocoa对象。注意CFBridgingRelease只是将CoreFoundation保留计数减1,并将其移动到Objective-C的ARC中。

由于以下4个问题,我在我的应用程序中遇到了内存泄漏:

  1. 在解散视图控制器时不使NSTimers无效
  2. 当解散视图控制器时忘记删除NSNotificationCenter的任何观察者。
  3. 在块中保持对self的强引用。
  4. 在视图控制器属性中使用对委托的强引用

幸运的是,我看到了下面的博客文章,并能够纠正它们:http://www.reigndesign.com/blog/debugging-retain-cycles-in-objective-c-four-likely-culprits/

Xcode 9提供了一个很好的工具来发现这类问题。它被称为:&;__abc0 &; 使用它,你可以通过类类型找到泄露的对象,你可以清楚地看到谁持有对它的强引用,通过从那里释放它解决了你的问题。

看更多关于如何使用它的信息