【问题标题】:Need to release twice?需要发布两次吗?
【发布时间】:2016-06-04 15:26:29
【问题描述】:

我似乎无法在任何地方找到答案。我在为 iOS 开发的 Objective-C 中使用手动内存管理。

我编写了一个从十六进制字符串中获取 UIColor 的便捷函数。在其中,它返回

[[UIColor alloc] initWithRed:... alpha:alpha]

显然在某些平台上(我们有一些设备,包括 iOS 8-9),该对象将在退出函数时被销毁,因此无法使用其返回的 UIColor*。所以现在,我们把它改成

[[[UIColor alloc] initWithRed:... alpha:alpha] retain]

我的问题是,当我使用完这个对象后,我必须释放它两次吗?一次分配,一次保留?这对我来说似乎很奇怪,我在网上找不到任何地方。

如果我不保留,它会在退出函数(在某些平台上)时被释放,从而使函数无用。如果我保留,我需要在完成后释放两次?

编辑:

https://developer.apple.com/library/ios/documentation/General/Conceptual/DevPedia-CocoaCore/MemoryManagement.html

"...,通常保证在接收它的方法或函数内保持有效。如果您希望它在该范围之外保持有效,则应保留或复制它。"

所以我没有做任何不寻常的事情。文档说如果“我希望它在函数范围之外保持有效”,我“应该保留它”。我会尝试@FreeNickname 的建议。这是最有意义的。

【问题讨论】:

  • 在那些平台上发布两次会崩溃吗?我会尝试使用[[[[UIColor alloc] initWithRed:... alpha:alpha] retain] autorelease],并将函数返回的UIColor 视为“已保留”(应该如此)。但在这种情况下,您必须在其他地方发布它,并且它会被发布两次。真是奇怪的情况。
  • “我在 iOS 的 Objective-C 开发中使用手动内存管理。”我不得不问:为什么?没有必要,而且您显然不知道自己在做什么。使用弧!编译器比你更了解内存管理。
  • "显然在某些平台上......该对象将在退出函数时被销毁,因此无法使用其返回的UIColor。"不,在手动引用计数中,使用alloc 实例化的对象在与相应的releaseautorelease 配对之前不会被释放。不要随意插入retain 语句。如果您对为什么认为需要添加 retain 有疑问,那么让我们专注于该问题,而不是尝试管理在插入随机 retain 语句时必须执行的操作。
  • 您确实需要更好地解释在哪些特定情况下预期行为未按预期运行。通过alloc/init 创建的对象不仅仅是在某些平台上无缘无故地被神秘地释放。这比你迄今为止提供的更多。专注于实际问题,而不是添加更多问题。
  • 不,您根本不需要保留它。 alloc/init 已经将保留计数设置为 1。在保留计数变为 0 之前,它不能被释放。假设您真的在使用 MRC,它不会神奇地变为 0,只是离开该方法。而且您的编辑并未尝试执行我在之前的评论中所说的内容。

标签: objective-c memory-management manual-retain-release


【解决方案1】:

你说:

我写了一个方便的函数来从一个十六进制字符串中获取UIColor。在其中,它返回

[[UIColor alloc] initWithRed:... alpha:alpha]

根据Basic Memory Management Rules,正确的内存管理取决于您的方法名称:

  • 如果你的方法名不是以“alloc”、“new”、“copy”或“mutableCopy”开头,那么你应该返回一个autorelease对象:

    - (UIColor *)colorWithHexString:(NSString *)hexString {
        ...
        return [[[UIColor alloc] initWithRed:... alpha:alpha] autorelease];
    }
    
  • 如果您的方法名称确实以“alloc”、“new”、“copy”或“mutableCopy”开头,那么您可以像上面一样返回一个对象:

    - (UIColor *)newColorWithHexString:(NSString *)hexString {
        ...
        return [[UIColor alloc] initWithRed:... alpha:alpha];
    }
    

    请注意,这种模式不如上述colorWithHexString 的约定常见。

(请注意,这种由方法名称前缀规定的内存管理在历史上只是最佳实践,但现在,为了与 ARC 代码的互操作性,这一点至关重要。在手动引用计数代码中始终遵循上述规则。)

现在,如果调用便捷初始化程序的代码允许释放对象,则问题出在该代码上,而不是您的便捷初始化程序。不要开始向初始化程序添加额外的 retain 语句,因为调用它的东西不能正确管理它的内存。

相反,请确保调用代码对colorWithHexString 本身的结果执行正确的retain(以及最终的release)。

顺便说一句,Xcode 的静态分析器(shift+command+B)非常擅长分析手动引用计数代码并识别问题。


在编辑您的问题时,您引用了文档:

如果您从程序的其他地方收到一个对象,通常保证它在接收它的方法或函数中保持有效。如果您希望它在该范围之外保持有效,您应该retain 或@987654333 @ 它。如果你试图释放一个已经被释放的对象,你的程序就会崩溃。

这并不是说您的便利初始化程序应该发出retaincopy。就是说调用colorWithHexString 的代码负责建立自己的UIColor 对象所有权声明,该对象通过retaincopy 返回,如上所述。

【讨论】:

    【解决方案2】:

    我认为您正在寻找autorelease 的概念,该概念用于像您这样的情况。它本质上是一种将延迟的release 消息发送到新创建的对象的方法,因此调用者有机会在必要时retain 它,否则在处理autoreleasepool 时它会被销毁。

    【讨论】:

      【解决方案3】:

      您“误解”了 Apple 的文档,因为它对于这个主题完全错误。你真的应该阅读 clang 的关于 ARC 而不是 Apple 的文档,因为 clang 的 ARC 文档正确解释了 MRC 以与它交互。

      让我们仔细看看:

      您拥有通过为它分配内存或复制它创建的任何对象。

      相关方法:allocallocWithZone:copycopyWithZone:mutableCopymutableCopyWithZone:

      相反,如果您不是对象的创建者并且未表达所有权权益,则不得释放它。

      如果您在程序中从其他地方接收到一个对象,通常保证它在接收到它的方法或函数中保持有效。

      认真对待此文档,您不是对象的所有者:

      [[UIColor alloc] initWithRed:... alpha:alpha]
      

      这是因为您确实没有+alloc 等人那里接收对象引用,而是从-init… 接收对象引用。按照 Apple 的文档,您不是所有者,必须保留它。 (所以它是“其他地方”。)

      在 clang 的文档中是 described differently and correctly:

      init 系列中的方法隐式使用它们的 self 参数并返回一个保留对象。 (5.2.1)

      因此,-init… 有一个特殊的方法族,以及 Apple 文档中提到的其他方法族 correctly described in clang's documentation

      家庭及其附加限制是:

      • alloc 方法必须返回可保留的对象指针类型。 [苹果:alloc,allocWithZone:)

      • 复制方法必须返回一个可保留的对象指针类型。 [苹果:copycopyWithZone:)

      • mutableCopy 方法必须返回可保留的对象指针类型。(Apple:mutableCopymutableCopyWithZone:

      • new 方法必须返回可保留的对象指针类型。 (Apple:哎呀,我忘记了什么)

      • init 方法必须是实例方法并且必须返回一个 Objective-C 指针类型。 ……(苹果:哎呀,我忘记了什么)

      (5.)

      所以,你从-init 得到的东西已经被保留了,你拥有所有权,绝对没有理由保留它。

      根据 Rob 的回答,可能有理由自动释放它。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-07-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多