【问题标题】:In Objective-C, the ownership is for object, not variable or pointers?在 Objective-C 中,所有权是对象,而不是变量或指针?
【发布时间】:2012-05-13 13:02:14
【问题描述】:

在一本书中是这样说的:

那么,您如何知道一个对象何时拥有,以及由谁拥有?考虑 下面的例子:

NSString *str = [[NSString alloc] initWithString:@”Hello”];  
NSString *str2 = str;

在此示例中,您将 alloc 关键字用于 str,因此您拥有 str。 因此,您需要在不再需要它时释放它。然而, str2 只是指向str,所以你不拥有str2,这意味着你 使用完毕后无需释放str2

我认为所有权是对象,而不是变量或指针...所以我们不能说我们“拥有str”或“拥有str2”...我们拥有一个对象,它由strstr2,如果我们使用[str release][str2 release],都是一样的。

另一种描述是:

例如,考虑上一节中使用的示例:

NSString *str = [[NSString alloc] initWithString:@”Hello”]; 
NSString *str2 = str;
[str release];
[str2 release]; //---this is not OK as you do not own str2---

尝试释放str2 将导致运行时错误,因为您 无法释放不属于您的对象。

如果在[str release] 之前调用,我们实际上可以使用[str2 release]。如果我们这样做,那么[str release] 行将导致错误,因为现在strstr2 都是悬空指针,并且假设当release 第一次发送到对象时,引用计数变为0,并立即调用dealloc,并由C函数free()释放内存。

以上是否正确,或者还有什么需要更正的地方?

【问题讨论】:

  • +1,但考虑在新代码中使用ARC
  • 你是对的。这本书要么非常混乱,要么完全错误。
  • 顺便问一下这是什么书?

标签: objective-c


【解决方案1】:

不要从管理内存的角度考虑它,而是从对象所有权的角度考虑。当您分配、保留或复制对象时,您将获得对象的所有权。您有责任完全释放您拥有的对象,而不是其他人。

在您的示例中,对str2 的分配不获取对象的所有权,但如果您确实需要对它的第二个“拥有”引用,那么您应该执行[str2 retain],之后它不是错误做[str release]; [str2 release];。这也是使用 ARC 会自动发生的情况,除非您将 str2 注释为弱引用。 (当然,在这种简单的情况下,编译器可以在内部优化掉不必要的保留/释放。)

【讨论】:

    【解决方案2】:

    你的猜测是正确的:这本书使用模糊语言(即使含义是正确的)来简化指针:

    您拥有strstr2 指向的对象。

    这当然意味着您只能释放对象一次(或者更确切地说,它经常被保留 - 在您的示例中,隐式地由alloc 保留一次),无论您是通过str 还是str2 这样做(或任何其他方式)是微不足道的。

    不过,在实践中,您应该将变量视为您拥有它们。这使得跟踪保留/释放对变得更加容易,并且您不能依赖这样一个事实,即没有人在两者之间的某个地方更改这些变量之一的值。

    之后将所有指向已释放实例的变量设置为nil 是一种很好的做法(但不是必需的)。

    深入研究技术细节(实际上应该将其视为:实现细节;私有 API 的工件):

    从技术上讲,没有人拥有该对象。该对象有一个保留次数的计数器(您可以通过调用anObject retainCount 找到它 - 但您不应该这样做,尤其是因为某些对象有一个虚假的retainCount,而且因为它真的与您无关) .当一个对象为alloced时,它的retainCount为1。每次发送retain("它被保留"),它的retainCount就加1,每次发送release("它是释放"),它的retainCount减1。

    一旦对象的 retainCount 达到零,它就会被释放(并调用其dealloc 方法)。

    谁发送了所有retain/release 消息(以及通过哪些变量)并不重要。

    再次重申:这些是实现细节。它们是 Objective-C/Cocoa 进行内存管理的方式的产物。像对待任何私有 API 一样对待它们:很好奇,但永远不要依赖内部。仅在生产代码中使用公共 API(在本例中为 retain/release 和自动释放池)。

    注意:一些对象(例如一些单例)会覆盖默认的保留/释放方法。永远不要相信你从一个对象中得到的retainCount,除非是出于好奇(例如,看看[UIColor clearColor] 的retainCount)。

    有关此主题的更多想法,this question 及其答案可能是一个不错的总结/起点。

    也就是说,考虑切换到 ARC,这将消除您几乎所有的内存管理问题。

    【讨论】:

    • 所有权的概念实际上是推理引用计数的正确方法,不是所涉及的实际数字。 str 是一个拥有引用,而 str2 不是,尽管它们指向同一个对象。将release 发送到str2 在语义上是不正确的,因为您没有通过所有权授予方法获得该引用。
    • @Jacques 我希望我已经明确表示实际的 retainCount 只是一个奇怪的实现细节。您是对的,尽管最好的做法是通过您保留对象的相同变量来释放对象(尤其是因为在非平凡的示例中,其中一个变量现在可能指向不同的对象);尽管如此,所有权“只是”一个概念,而不是技术真理,恕我直言。您非常好的问题和stackoverflow.com/questions/5784084/… 的答案可能也会满足您对此的一些好奇。
    • 好吧,你在答案的后半部分开始了你的回答,在那里你谈到了保留计数并使用 retainCount 和“变得更加正确”,但事实并非如此。引用计数的“技术真理”(如您所说)与用户无关 - 所有权是意义所在的更高级别以及应在何处做出决定。在那个级别上,您使用哪个指针发送release 绝对确实会有所不同。它只是在较低级别,您应该(通常)忽略,您可以通过任何您喜欢的参考发送release
    • @Jacques 我不想在这里留下虚假信息,所以我编辑了我的答案。想发表评论吗?
    【解决方案3】:

    我认为所有权是对象,而不是变量或指针...所以我们 不能说我们“拥有 str”或“拥有 str2”……我们拥有一个对象,即 由 str 或 str2 指向,如果我们使用 [str release] 或 [str2 release],都是一样的。

    这是正确的。

    我认为作者所说的“我们拥有 str”的意思是您拥有该字符串实例。不是指针或变量。理论上,您可以使用其他指针释放对象。但通常最好使用用于初始化对象的变量来释放。

    【讨论】:

    • 为什么使用创建它的指针释放内存中的对象会更好?这两个指针是相等的,所以你可以选择使用哪一个来释放对象。
    • @Hidde:这在技术上并不重要,但如果你在使用它时保持一定的纪律,它会更容易跟踪保留/释放。
    • 好的,没错。但是,如果我们谈论的是良好的编程习惯,为什么要创建两个指向对象的指针呢?如果有任何理由这样做,也许还有一个理由使用第二个指针释放对象。
    • 你是对的,可能有一些极端情况。但正如我所说,“通常”最好使用相同的变量来发布。
    • 这个答案具有误导性。请参阅我的答案以获得澄清。
    【解决方案4】:

    我认为其他答案是错误的或不完整的。

    所有权由释放资源的人定义。一个对象(通常)不拥有自身,它是拥有。 p>

    再次重申:所有者是负责释放内存的人。

    在您的代码中,str 已声明其对对象的所有权,str2 尚未声明。为了让str2 也拥有该对象(分享 str 的所有权),您需要retain 它:

    [str2 retain];
    

    现在你可以说,

    [str2 release];
    

    放弃str2 的所有权要求,str 也是如此。

    另一方面,对于 ARC,all(引用计数)指向资源的指针是它的共享所有者。在这种情况下,所有指针负责跟踪对象引用计数,并在确定它们是唯一所有者并超出范围时释放对象。

    重申:所有权不是对象。所有权是由指针一个对象,以及这些指针的所有者,但只有if这些指针可能在某些情况下被释放。如果不存在释放资源的情况,则指针是非拥有的。在这种情况下,它们被称为 weak

    如果对象拥有自己,弱指针的概念将毫无意义(因为所有指针都是弱指针)。

    【讨论】:

      【解决方案5】:

      如果您释放strstr2,则strstr2 指向的内存位置将被释放。因此,您只需调用strstr2,而不是同时调用两者,因为它们指向同一个内存位置。

      我不明白你所说的“拥有”一个变量是什么意思,据我所知,指针可以指向内存中的一块空间,仅此而已。

      【讨论】:

      • 我使用短语“拥有一个变量”,因为作者似乎建议我们“拥有str”而不是拥有str2
      • “拥有一个对象”的概念在编程中被广泛使用,指的是对一个对象占用的内存空间的责任。如果你“拥有”一个对象,你就有责任清理它的内存。
      • 那样...我认为这很令人困惑,因为str2 可以与str 指向的对象完全相同。如果我们调用[str2 release],它的意思和[str release]完全一样。如果您使用“自己的”来指代在内存中保留空间以创建对象的操作,那么似乎一个指针对对象具有更多的权力/特权。它没有,所以很混乱。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-08
      • 2022-01-11
      • 1970-01-01
      相关资源
      最近更新 更多