在 Mac 和 iOS 平台中,内存泄漏通常是由未释放的指针引起的。传统上,检查您的分配、复制和保留以确保每个都有相应的发布消息一直是最重要的。
Xcode 4.2 附带的工具链在最新版本的 LLVM compiler 中引入了自动引用计数 (ARC),通过让编译器为您管理您的内容,完全解决了这个问题。这很酷,它确实减少了许多不必要的、平凡的开发时间,并防止了许多粗心的内存泄漏,这些泄漏很容易通过适当的保留/释放平衡来修复。当您为 Mac 和 iOS 应用程序启用 ARC 时,即使是自动释放池也需要以不同的方式进行管理(因为您不应再分配自己的 NSAutoreleasePool
)。
但是还有哪些其他内存泄漏不能阻止我仍然需要提防?
作为奖励,Mac OS X 和 iOS 上的 ARC 和 Mac OS X 上的垃圾收集有什么区别?
您仍然需要注意的与内存相关的主要问题是保留周期。当一个对象具有指向另一个对象的强指针,但目标对象具有指向原始对象的强指针时,就会发生这种情况。即使删除了对这些对象的所有其他引用,它们仍然会相互保持并且不会被释放。这也可以通过一系列对象间接发生,这些对象链中的最后一个对象可能指向较早的对象。
正是由于这个原因,存在 __unsafe_unretained
和 __weak
所有权限定符。前者不会保留它指向的任何对象,但会保留该对象消失并指向坏内存的可能性,而后者不保留该对象并在其目标被释放时自动将其自身设置为 nil。在这两者中,__weak
通常在支持它的平台上是首选。
您可以将这些限定符用于委托等事物,您不希望对象保留其委托并可能导致循环。
另一个与内存相关的重要问题是处理 Core Foundation 对象和使用 malloc()
为 char*
等类型分配的内存。 ARC 不管理这些类型,只管理 Objective-C 对象,因此您仍然需要自己处理它们。 Core Foundation 类型可能特别棘手,因为有时它们需要桥接以匹配 Objective-C 对象,反之亦然。这意味着在 CF 类型和 Objective-C 之间进行桥接时,需要从 ARC 来回传输控制。添加了一些与此桥接相关的关键字,Mike Ash 在 his lengthy ARC writeup 中对各种桥接案例进行了很好的描述。
除此之外,还有其他一些不太常见但仍有潜在问题的案例,published specification 对此进行了详细介绍。
许多新行为基于只要有指向它们的强指针就保留对象,这与 Mac 上的垃圾收集非常相似。但是,技术基础却大相径庭。这种内存管理风格不是让垃圾收集器进程定期运行以清理不再指向的对象,而是依赖于我们在 Objective-C 中都需要遵守的严格的保留/释放规则。
ARC 只需将我们多年来不得不做的重复性内存管理任务卸载到编译器,这样我们就不必再担心它们了。这样,您就不会遇到在垃圾收集平台上遇到的停止问题或锯齿状内存配置文件。我在垃圾收集的 Mac 应用程序中经历了这两种情况,并渴望了解它们在 ARC 下的表现。
有关垃圾收集与 ARC 的更多信息,请参阅 this very interesting response by Chris Lattner on the Objective-C mailing list,其中他列出了 ARC 相对于 Objective-C 2.0 垃圾收集的许多优点。我遇到了他描述的几个 GC 问题。
ARC 不会帮助您处理非 ObjC 内存,例如,如果您malloc()
某事,您仍然需要free()
它。
如果编译器无法确定选择器是什么,ARC 可能会被 performSelector:
愚弄(编译器会对此产生警告)。
ARC 还将生成遵循 ObjC 命名约定的代码,因此,如果您混合使用 ARC 和 MRC 代码,如果 MRC 代码没有按照编译器认为的名称承诺执行,您会得到令人惊讶的结果。
由于以下 4 个问题,我的应用程序中出现了内存泄漏:
关闭视图控制器时不会使 NSTimers 失效 关闭视图控制器时忘记删除 NSNotificationCenter 的任何观察者。在块中保持对自我的强烈引用。在视图控制器属性中使用对委托的强引用
幸运的是,我看到了以下博客文章并能够更正它们:http://www.reigndesign.com/blog/debugging-retain-cycles-in-objective-c-four-likely-culprits/
ARC 也不会管理 CoreFoundation 类型。您可以“桥接”它们(使用 CFBridgingRelease()
),但前提是您要将其用作 Objective-C/Cocoa 对象。请注意,CFBridgingRelease 只是将 CoreFoundation 保留计数减 1 并将其移动到 Objective-C 的 ARC。
Xcode 9 为查找此类问题提供了一个很好的工具。它被称为:“Debug Memory Graph”。使用它,您可以按类类型找到您泄漏的对象,并且您可以清楚地看到谁拥有对它的强引用,通过从那里释放它可以解决您的问题。它还检测内存周期。
See more info about how to use it