【问题标题】:Correct approach for checking memory allocations in Objective C在 Objective C 中检查内存分配的正确方法
【发布时间】:2011-06-02 10:41:35
【问题描述】:

我想知道在执行在 Objective C 中分配内存的命令后正确的方法是什么(我主要指的是 iOS 应用程序)。 我的困境来自这样一个事实,即检查内存分配操作是否成功会增加很多代码行,同时想知道这是否有用。 此外,有时内存分配是显而易见的,例如使用“alloc”,但有时它们是在幕后进行的。即使我们检查每一个分配——当我们发现它失败时——我们实际上也无能为力。所以也许正确的方法是让它失败并让应用程序崩溃? 看看这段代码:

// Explicit memory allocation
NSArray a1 = [[NSArray alloc] initWithObjects:someObj, nil];
if (!a1) {
  // Should we make this check at all? Is there really what to do?
}

// Implicit memory allocation
NSArray a2 = [NSArray arrayWithObjects:someObj, nil];
if (!a2) {
  // should we make this check at all? Is there really what to do?
}

您认为正确的方法是什么?检查或不检查分配失败?那里的 iOS 开发人员 - 你是如何在你的应用程序中处理它的?

【问题讨论】:

    标签: iphone objective-c memory ios memory-management


    【解决方案1】:

    检查它们,在调试中断言(这样您就知道故障存在的位置/原因),并将错误推送到客户端(在大多数情况下)。客户端通常会有更多的上下文——他们会用更小的请求重试吗?以某种方式失败?禁用某个功能?向用户等显示警报。您提供的示例并非世界末日,您可以从许多示例中优雅地恢复 - 此外,您需要(或应该知道)您的程序何时何地失败。

    【讨论】:

    • 这在你知道你将要分配一大块内存的地方是正确的。在这种情况下,您最好检查结果,如果可以的话,可能会做其他事情。但我指的是在你的代码的每一行中发生的所有那些小分配。那里的失败意味着您尝试向用户显示消息也将失败。另请参阅下面我对 Jason 的回答。
    • 1) 您最初的问题不限于小分配 2) 很有可能,您不会分解每个调用来确定什么是“小分配” - 背后发生了很多事情场景 3)你认为/写的是一个 nomem 条件会因为其他原因而更频繁地失败,例如无效的参数/上下文。如果您不打算在使用前进行简单的空检查...我绝对不希望您编写/维护所有这些条件(尤其是当您没有源代码时)。 4) 优雅恢复是一种选择,您应该知道/纠正应用程序的故障。
    • 5) 失败也意味着您可以在日志消息或崩溃报告中定位问题。
    • 我不确定我是否完全理解你,也许我最初的问题有些不清楚。如果我亲自问您 - 您是否在每个可能因内存问题返回 null 的操作之后检查 null (例如我最初给出的示例)?如果你这样做了,你觉得这是正确的做法吗?
    • 我要检查吗?是的,在大多数情况下(包括您给出的示例)。我没有 100% 的覆盖率,但大多数点都被检查了——尤其是在代码被重用的地方。错误测试/处理通常超过 SLOC 中函数/方法主体的一半,但这种正确程度使缺陷数量保持在较低水平。积极因素通常会发现内存不足问题以外的问题。内存不足的情况非常罕见。我认为这是正确的方法吗?是的,我想知道程序失败的位置和原因 - 大多数缺陷都可以解决。
    【解决方案2】:

    凭借您在 Iphone/智能手机中的强大功能,计算一些测试所花费的时间对于思考“是否真的值得检查”是荒谬的,它总是很好的测试并发现代码/分配中的任何故障. (如果你不这样做,这听起来更像是你懒得在代码中添加几行。

    此外,“让应用程序崩溃”也会给您的应用程序留下非常糟糕的印象,用户看到应用程序无缘无故关闭,并认为它是一个质量很差的软件。 你应该总是添加你的测试,如果你不能对错误做任何事情,那么至少你应该在应用关闭之前显示一条消息(让用户不那么沮丧)。

    在跟踪内存分配时有几个选项,例如捕获异常。测试返回的指针是否为 nil,检查列表的大小等。

    你应该想办法让你的应用程序在分配失败的情况下运行:

    • 如果它只是您的界面视图,则显示一条消息,说明无法加载特定视图...

    • 如果是主视图,也是唯一的视图,用消息优雅地关闭应用程序

    ...

    我不知道您正在开发什么应用程序,但如果您的内存不足,您应该考虑创建一个系统来在您的应用程序中分配释放的内存,以便您始终拥有最大的可用内存。它可能比保留所有内容缓存稍慢,但如果您抑制任何强制关闭,您的应用程序质量将会提高。

    【讨论】:

    • 如果您无法为 20 字节对象分配内存,祝您好运:-)
    • 正是...如果您未能分配一个简单的数组,您将无法显示消息...我认为现实是人们“认为”他们已经涵盖他们自己从内存分配问题,但他们从来没有真正测试过这些情况(因为你不能在你的代码的每一行中真正模拟内存分配失败。因此在实践中如果发生这种情况 - 也许你覆盖了第一个失败,但你会结束进一步失败了几行。不知何故,看起来我们确实做了很多工作来解决它,检查这些失败,而实际上它是无用的。
    • 我的意思是你应该尝试处理最多的案件,而不是采取“既然失败了,我就让它崩溃”的态度
    • 是的,显然我完全同意“让它崩溃”的方法不是理想的方法。但我实际上是在寻找一种更好的方法来做到这一点,而不是在微观层面上处理每一次分配。例如 - 附加一个通用异常处理程序来捕获所有故障,或类似的东西。事实上,在我的应用程序中,我确实使用了一个通用处理程序,但我同时检查了每个分配。不知何故,这似乎不是理想的方式。另外——Objective-C 支持 try/catch,但我注意到由于某种原因它很少使用。这个问题有些不妥。
    • 如果分配失败,“让它崩溃”是唯一的办法。无法保证任何底层系统状态都足够一致,可以向用户报告任何有用的信息。
    【解决方案3】:

    幻想:每个内存分配都会被检查,任何失败都会以友好的方式报告给用户,应用程序会干净地关闭,会发送错误报告,你可以修复它并且下一个版本将是完美的[在那种情况下]。

    现实:当像arrayWithObjects: 这样微不足道的事情失败时,你的应用程序早就死了。在这种情况下没有恢复。框架很可能已经分配失败并且已经破坏了您的应用程序的状态。

    此外,一旦像arrayWithObjects: 这样的基本操作失败了,您无论如何都无法告诉用户。如果没有进一步的分配,您将无法可靠地将对话框显示在屏幕上。

    但是,在您的应用分配失败之前,失败发生得更远。也就是说,您的应用应该收到内存警告,并且应该通过 (a) 保持状态以确保不会丢失客户数据并 (b) 释放尽可能多的内存以避免灾难性故障。

    不过,内存警告是内存使用战争中最后一道可行的防线。

    您对减少内存的第一次攻击是在设计和开发过程中。您应该从应用程序开发过程的一开始就考虑内存使用情况,并且必须在优化应用程序以供发布时优化内存使用情况。使用分配工具(请参阅我之前做过的Heapshot analysis write-up,它非常适用)并证明每个主要的内存消耗者的存在。

    【讨论】:

    • 完全同意我们需要努力减少内存消耗。我也为此目的使用了 Instruments。我仍然想知道是否检查每一行中的内存故障是编码的方式。在您的应用程序中 - 您是否在每次小内存操作后检查内存故障?
    • 不;我检查nilNULL 分配返回的唯一一次是当存在某种与内存分配相关的逻辑时(即newWithSomethingThatMightBeInvalid:)可能会故意返回NULL/nil。尝试这样做,比如malloc() 或类似的东西是完全没有意义的。
    【解决方案4】:

    iPhone 应用程序应注册 UIApplicationDidReceiveMemoryWarningNotification 通知。当可用内存不足时,iOS 会发送这些。 Google iphoneappprogrammingguide.pdf(2011 年 10 月 12 日)了解更多信息。

    也就是说,我看到的解决问题的一种通用方法是在应用启动时保留一块内存作为“缓冲”。在您的代码中,在每次分配后进行测试。如果分配失败,请释放缓冲,以便您有足够的内存来显示错误消息并退出。缓冲垫的大小必须足够大,以容纳您的应用程序顺利关闭。您可以使用内存压力测试仪来确定缓冲垫的大小。

    这确实是一个棘手的问题,因为它很少发生(对于精心设计的程序)。在 PC/迷你/大型机世界中,虚拟内存几乎消除了但最病态的程序中的问题。在内存有限的系统(如智能手机)中,使用堆监控工具对应用程序进行压力测试应该可以很好地指示其最大内存使用量。你可以为 alloc 编写一个高水位标记包装例程来做同样的事情。

    【讨论】:

    • 很多对这个问题的回答,包括我上面自己的,都说要优雅地退出。好吧,据我所知,没有 API 可以退出 iOS 应用程序。官方流程是通过覆盖appdelegate或UIViewController中的指定方法或注册通知来响应内存不足警告。 (请参阅上面提到的 Apple 的 iPhone 编程指南。)此时,您可以尽可能多地释放分配的内存和/或提醒用户。这么说感觉很异端,但只要您响应内存不足警告,您实际上并不需要在每次分配后进行测试。
    猜你喜欢
    • 2013-07-30
    • 1970-01-01
    • 1970-01-01
    • 2012-01-22
    • 1970-01-01
    • 2010-11-02
    • 2013-10-14
    • 2012-08-15
    • 2019-02-11
    相关资源
    最近更新 更多