问题 在Objective-C中,所有权是对象,而不是变量或指针?


在一本书中,说如下:

那么你怎么知道对象何时拥有,以及由谁拥有?考虑一下   以下示例:

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

在此示例中,您使用 alloc 关键字 str,所以你拥有 str。   因此,您需要在不再需要时释放它。然而,    str2 只是指着 str,所以你不拥有 str2,意思是你   不需要发布 str2 当你完成使用它。

我认为所有权是按对象而不是变量或指针...所以我们不能说我们“拥有” str“或”拥有 str2“......我们拥有一个物体,任何一个物体都指向它 str 要么 str2,如果我们使用 [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 将导致运行时错误,因为您   无法释放不属于您的对象。

我们实际上可以使用 [str2 release] 如果以前叫过 [str release]。如果我们这样做,那么就行 [str release] 因为现在会导致错误 str 以及 str2 都是悬挂的指针,据说是什么时候 release 第一次被发送到对象,引用计数变为0,并且 dealloc 被立即调用,内存被C函数释放 free()

以上是否正确,或者还有其他问题需要纠正?


9633
2018-05-13 13:02


起源

+1,但考虑使用 弧 在新的代码中。 - rid
+1,好问题,顺便说一句。
你是对的。这本书要么是非常混乱,要么是错误的。 - Manlio
顺便问一下这本书是什么书? - fzwo


答案:


不要在管理内存方面考虑它,而是在对象所有权方面。在分配,保留或复制对象时,您将获得对象的所有权。您有责任准确发布您拥有的对象,而不是其他对象。

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


5
2018-05-13 13:33



这是唯一完全正确的答案。 - Josh Caswell


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

你拥有那个对象 str 和 str2 指向。

这当然意味着你只能释放一次对象(或者更确切地说,它被保留了 - 一次在你的例子中,隐含地,通过 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 你从一个物体获得,而不是好奇心(看看的retainCount) [UIColor clearColor] 例如)。

有关此主题的更多想法, 这个问题 它的答案可能是一个很好的总结/起点。

那说, 考虑切换到ARC,这将摆脱你几乎所有的内存管理麻烦。


3
2018-05-13 13:10



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


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

这是对的。

我认为作者所说的“我们拥有str”意味着你拥有那个字符串实例。不是指针或变量。理论上你可以使用另一个指针释放对象。但是使用用于初始化对象的变量来释放通常是个更好的主意。


2
2018-05-13 13:09



为什么使用创建它的指针在内存中释放对象会更好?两个指针是相同的,因此您可以选择使用哪个指针来释放对象。 - Hidde
为了更好的概述和易于理解的代码。 - DrummerB
@Hidde:技术上并不重要,但是如果你在使用它时保持一定的纪律,它会更容易跟踪保留/释放。 - fzwo
好的,那是真的。但是,如果我们谈论良好的编程习惯,为什么你会指向对象的两个指针?如果有任何理由这样做,也许还有一个原因是使用第二个指针释放对象。 - Hidde
你是对的,可能会有一些极端情况。但正如我所说,“通常”最好使用相同的变量发布。 - DrummerB


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

所有权由谁发布资源定义。 对象不(通常)拥有 本身, 它是 拥有的

再说一遍:所有者是负责释放记忆的人。

在你的代码中, str 宣布了对象的所有权, str2 没有。为了 str2 也拥有对象(到 分享  str的所有权),你需要 retain 它:

[str2 retain];

现在你可以稍后说,

[str2 release];

放弃 str2对所有权的主张,同样适用 str

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

重申:所有权是  按对象。所有权是指针  一个对象,以及那些指针的所有者,但仅限于此 如果 在某些情况下可能会发布这些指针。如果没有方案可以释放资源,则指针是非拥有的。在这种情况下,他们被称为

一个概念 弱指针 如果对象拥有自己是没有意义的(因为所有指针都很弱)。


1
2018-05-13 18:54





如果你释放 str 要么 str2,内存位置 str 和 str2 指向被释放。所以,你只需要打电话 str 要么 str2而不是两者,因为它们指向相同的内存位置。

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


0
2018-05-13 13:07



我使用短语“拥有一个变量”,因为作者似乎暗示我们“拥有” str“而不是拥有 str2 - Jeremy L
“拥有对象”的概念在编程中被广泛使用,以指代对象占用的内存空间的责任。如果你“拥有”一个物体,你就有责任清理它的记忆。 - rid
那样......我觉得这很令人困惑,因为 str2 可以与它指向的对象完全相同 str 能够。如果我们调用[str2 release],它意味着完全相同 [str release]。如果您使用“自己”来指代在内存中保留空间以创建对象的操作,则似乎一个指针在该对象上具有更多的权力/特权。它没有,所以令人困惑。 - Hidde