避免、发现和消除 Cocoa 中的内存泄漏

Posted

技术标签:

【中文标题】避免、发现和消除 Cocoa 中的内存泄漏【英文标题】:Avoiding, finding and removing memory leaks in Cocoa 【发布时间】:2010-09-15 09:17:25 【问题描述】:

发生内存(和资源)泄漏。你如何确保他们没有?

您会建议哪些技巧和技术来帮助避免首先造成内存泄漏?

一旦您的应用程序发生泄漏,您如何追踪泄漏源?

(哦,请避免“只使用 GC”的答案。在 iPhone 支持 GC 之前,这不是一个有效的答案,即使那样 - 可能会在 GC 上泄漏资源和内存)

【问题讨论】:

【参考方案1】:

遵循保留和释放规则(或使用垃圾收集)。 They're summarized here.

使用仪器追踪泄漏。您可以使用 Xcode 中的 Build > Start With Performance Tool 在 Instruments 下运行应用程序。

【讨论】:

好答案。这两个技巧绝对是解决方案的一部分。我希望这篇文章成为内存泄漏调试的完整 A-Z。我们会看看这是否会发生。 ;-) 我认为 ARC 内存泄漏应该不是问题。我怎么还能拥有它? ARC 自动发送保留和释放消息,但您仍然必须考虑保留对象图并确保不会发生对象相互引用的循环。您可以使用 @property(weak) 或 __weak 来防止这种情况发生。【参考方案2】:

Instruments Leaks 工具非常擅长发现某种类型的内存泄漏。只需使用“Start with Performance Tool”/“Leaks”菜单项即可通过此工具自动运行您的应用程序。适用于 Mac OS X 和 iPhone(模拟器或设备)。

Leaks 工具可帮助您找到泄漏源,但对追踪泄漏内存的保留位置没有太大帮助。

【讨论】:

【参考方案3】:

我记得不久前我使用 Omni 的一个工具,当时我试图追踪一些内存泄漏,这些泄漏会显示对象上的所有保留/释放/自动释放调用。我认为它显示了分配的堆栈跟踪以及对象上的所有保留和释放。

http://www.omnigroup.com/developer/omniobjectmeter/

【讨论】:

【参考方案4】:

在 XCode 4.5 中,使用内置的Static Analyzer。

在 3.3 之前的 XCode 版本中,您可能需要下载静态分析器。这些链接向您展示了如何:

使用 LLVM/Clang 静态分析器

为避免一开始就造成内存泄漏,请使用Clang Static Analyzer -- 不出所料 -- 在 Mac OS X 10.5 上分析您的 C 和 Objective-C 代码(还没有 C++)。安装使用很简单:

    从this page下载最新版本。 从命令行,cd 到您的项目目录。 执行scan-build -k -V xcodebuild

(还有一些额外的限制等,特别是您应该在其“调试”配置中分析一个项目——有关详细信息,请参阅http://clang.llvm.org/StaticAnalysisUsage.html——但这或多或少是归结为的。)

分析器随后会为您生成一组网页,其中显示可能的内存管理和编译器无法检测到的其他基本问题。

如果您的项目不针对 Mac OS X 桌面,还有一些其他细节:

    将 Base SDK for All Configurations 设置为使用 Mac OS X 桌面框架的 SDK... 将命令行构建设置为使用调试配置。

(这与this question 的答案基本相同。)

【讨论】:

您好,先生,您的链接已失效。你能编辑和修复吗?【参考方案5】:

始终使用访问器方法;使用属性声明访问器

如果您总是使用访问器方法为实例变量赋值(init*dealloc 方法除外),那么您自己的生活就会变得更加简单。除了确保正确触发任何副作用(例如KVO change notifications)之外,与使用@987654325 撒上代码相比,它使您遭受复制粘贴或其他一些逻辑错误的可能性要小得多@s 和releases。

在声明访问器时,您应该始终使用Objective-C 2 properties 功能。属性声明使访问器的内存管理语义明确。它们还为您提供了一种简单的方法来交叉检查您的 dealloc 方法,以确保您已释放您声明为 retaincopy 的所有属性。

【讨论】:

【参考方案6】:

不要过度考虑内存管理

出于某种原因,许多开发人员(尤其是早期开发人员)使自己的内存管理比以往任何时候都更加困难,通常是通过过度思考问题或想象问题比实际情况更复杂。

fundamental rules 非常简单。您应该只专注于遵循这些。不要担心其他对象可能会做什么,或者您的对象的保留计数是多少。相信其他人都遵守相同的合同,这一切都会正常工作。

特别是,我将重申不要担心对象的保留计数这一点。由于各种原因,保留计数本身可能会产生误导。如果您发现自己记录了对象的保留计数,则几乎可以肯定您走错了路。退一步问问自己,你是否遵守基本规则?

【讨论】:

我还没有编辑权限,但是您链接的规则有一个可能随时消失的重定向。如果你用这个链接更新它,它会让你的好帖子保持新鲜和相关:developer.apple.com/mac/library/documentation/Cocoa/Conceptual/…【参考方案7】:

首先,您对 [ ] 和 括号和大括号的使用符合通用标准非常重要。好吧,开个玩笑。

查看泄漏时,您可以假设泄漏是由于代码中的问题造成的,但这并不是 100% 的错误。在某些情况下,Apple 的(喘气!)代码中可能发生了错误。而且它可能很难找到,因为它不会显示为被分配的可可对象。我过去曾向 Apple 报告过泄漏错误。

泄漏有时很难找到,因为您发现的线索(例如,数百个字符串泄漏)可能不是因为直接导致字符串泄漏的那些对象正在泄漏,而是因为某些东西正在泄漏 那个 对象。通常,您必须挖掘泄漏的“树”的叶子和树枝才能找到问题的“根源”。

预防:我的主要规则之一是真的,真的,真的避免分配一个对象,而不是当场自动释放它。任何你分配/初始化一个对象然后在代码块中释放它的地方都是你犯错的机会。要么你忘记释放它,要么你抛出一个异常以使释放永远不会被调用,或者你在方法的某处放置一个“return”语句以提前退出(我也尽量避免)。

【讨论】:

抱歉,Dan,但现在认为最佳做法是避免自动释放,尤其是在资源受限的平台上(请参阅 ***.com/questions/155964/…)。 随着 Clang 静态分析器 (clang.llvm.org/StaticAnalysis.html) 的出现,您正确识别的忘记释放或过度释放的问题通常应该很容易找到。【参考方案8】:

您可以从这里构建 Valgrind 的 beta 端口:http://www.sealiesoftware.com/valgrind/

它比任何静态分析都有用得多,但据我所知,它还没有任何特殊的 Cocoa 支持。

【讨论】:

【参考方案9】:

显然,您首先需要了解基本的内存管理概念。但在追查泄漏方面,我强烈建议阅读this tutorial on using the Leaks mode in Instruments。

【讨论】:

以上是关于避免、发现和消除 Cocoa 中的内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

如何减少/消除 Angular 应用程序中的内存泄漏

翻译: 如何使用 Xcode 的内存图调试器检测 iOS 内存泄漏并保留周期

Admob 内存泄漏 - 通过使用空活动来避免

使用 Cocoa 按钮打开 url 会导致内存泄漏

Cocoa框架中是否存在内存泄漏?或者我错过了什么?

如何防止java中的内存泄漏