【问题标题】:Objective-C pointers/memory management questionObjective-C 指针/内存管理问题
【发布时间】:2011-07-28 22:02:08
【问题描述】:

我正在测试下面的代码。 ffv 在接口文件中声明。

ffv = [[FullFunctionView alloc] initWithFrame:self.view.bounds];
NSLog(@"%i", [ffv retainCount]);  // prints 1
[self.view insertSubview:ffv belowSubview:switchViewsBtn];
NSLog(@"%i", [ffv retainCount]);  // prints 2
[ffv release]; // you can release it now since the view has ownership of ffv
NSLog(@"%i", [ffv retainCount]);  // prints 1

if (ffv == nil)
    NSLog(@"ffv is nil");

// "ffv is nil" is not printed

[ffv testMethod]; // "test method called" is printed

这是我的 [ffv testMethod] 实现

- (void)testMethod
{
    NSLog(@"test method called");
}

我在这种情况下的推断是,即使您释放了一个保留计数为 2 的对象,您也会失去该对象的所有权,但是仍然会保留该引用。

现在,我的问题是:

  1. 我的推论正确吗?
  2. 还有什么重要的可以从中推断出来的吗?
  3. 仍然保留(使用)ffv 并从 ffv 调用方法会导致哪些并发症? (我的观点是这没关系,因为视图将始终拥有 ffv,并且在有人调用 viewDidUnload 之前不会释放它。只要我不将 ffv 的引用传递给其他对象。)

【问题讨论】:

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


【解决方案1】:

在您发布 ffv 之后使用它会出现一些问题,并且它只会被您的视图控制器的视图保留。

1) 它引入了未来错误的可能性,因为以后您可能不记得 ffv 否则不会保留。当您释放视图时(例如,通过将其替换为另一个视图),您有一个悬空指针,您仍然持有对它的引用。

2) 在UIViewController 的特殊情况下,视图可以随时释放(您通常永远不会自己调用viewDidUnload)。 UIViewController 的默认行为,当接收到内存警告并且视图当前不可见时,是释放视图,因此除非您在 viewDidUnload 中将引用设置为 nil,否则您将再次有一个悬空指针,即使您自己从未明确发布过视图。

【讨论】:

    【解决方案2】:

    1) 我的推论正确吗?

    你的推论是正确的。 Memory Management Programming Guide 解释说每个对象都有一个或多个所有者。您拥有使用以allocnewcopymutableCopy 开头的任何方法创建的任何对象。您还可以使用保留获得对象的所有权。完成对象后,您必须使用 releaseautorelease 放弃所有权。

    释放对象不会更改引用该对象的任何变量的值。您的变量包含对象的内存地址,直到您重新分配它,无论对象具有多少保留计数。即使对象的保留计数变为零,导致对象被释放,您的变量仍将指向同一地址。如果您在对象被释放后尝试访问它,您的应用程序通常会因 EXC_BAD_ACCESS 而崩溃。这是一个常见的内存管理错误。

    2) 有没有其他重要的事情可以从中推断出来?

    什么都没想到。

    3) 仍然保留(使用)ffv 并从 ffv 调用方法会导致哪些并发症? (我的观点是这没关系,因为视图将始终拥有 ffv,并且在有人调用 viewDidUnload 之前不会释放它。只要我不将 ffv 的引用传递给其他对象。)

    当您调用release 时,您是在告诉Objective C 运行时您不再需要访问该对象。虽然在很多情况下您知道对象仍然存在,但实际上您确实不应该在调用release 之后访问对象。你只是在试探命运,为未来的错误做好准备。

    我个人不喜欢在我的代码中添加release 语句,因为我不相信自己能 100% 地记住它们。相反,我更喜欢像这样分配变量后立即自动释放变量:

    ffv = [[[FullFunctionView alloc] initWithFrame:self.view.bounds] autorelease];
    

    这保证了 ffv 至少会存在到方法结束。它将在此后不久发布,通常在运行循环的下一次迭代之前。 (理论上,如果您在紧密循环中分配大量临时对象,这可能会消耗过多的内存,但实际上我从未遇到过这种情况。如果我遇到过,它会很容易优化。)

    【讨论】:

    • 但是,问题在于 OP 的示例假设绝对 retainCount 是有意义的。它不是。目前的实现细节似乎没有将保留计数增加 2、5 或 42,这只是幸运的。
    • 我假设您指的是他的推论,即“如果您释放保留计数为 2 的对象,您将失去该对象的所有权,但是仍然保留引用。”我的意思是,“如果你释放了一个也被其他对象保留的对象,你就放弃了对该对象的所有权,但你的变量仍然指向它。”
    • 不,我是说任何使用retainCount 来收集任何含义的代码都是错误的。 retainCount 没有用,任何对象的绝对保留计数也是如此。保留计数应完全视为增量;如果您 +1 保留计数,则需要 -1 保留计数,否则您仍然“拥有”它。
    • 我明白了。我当然同意。
    • 是的版本不会让你的指针指向零。我使用保留计数的原因是为了确保自己视图保留了对象,并且只要视图仍然存在,该对象就不会被释放。是的,该对象可能会被其他类的函数保留,但这不是这个问题的重点。
    【解决方案3】:

    在保留计数变为 0 之前,该对象不会释放。只要它没有被释放,您就可以继续使用它而不会遇到麻烦。通过保留它,您可以确保它不会在您的脚下被释放;但是,如果您保留另一个您知道保留第一个对象的对象,则可以摆脱这种“间接保留”形式。但是,当您稍后搬动东西并且事情开始破裂时,请不要抱怨。

    【讨论】:

    • 您的意思是,在保留计数变为零之前,该对象不会解除分配。对象应该释放一次或多次(每次保留一次)。
    • 是的,我就是这个意思。感谢您了解这一点。
    【解决方案4】:
    if (ffv == nil)
        NSLog(@"ffv is nil");
    // "ffv is nil" is not printed
    

    没错,释放一个对象不会将指针设置为零即使它当时被释放。好的做法是在释放指针后始终将指针设置为 nil。 你说得对,在你释放它之后,它并没有被释放,因为视图仍然保留它。但这不是你应该考虑的方式。如果您想使用该对象并且希望它还活着,请保留它。别人保留它并不重要。您的对象与其他对象无关。你想要它,保留它。你完成了它,释放它并将你的指针设置为nil。如果你不将它设置为 nil 并且其他人也释放了它,你将有一个悬空指针指向一个被释放的对象,这将导致你崩溃和很多委屈。

    这样:

    [ffv release]; // you can release it now since the view has ownership of ffv
    ffv = nil;  // you released it, so that means you don't want it anymore, so set the pointer to nil
    

    如果你还想使用它,在你用完之前不要释放它。

    【讨论】:

    • +1。为此“但这不是您应该考虑的方式。如果您想使用该对象并且希望它还活着,请保留它。还有谁在保留它无关紧要。”
    【解决方案5】:

    嗯,我不确定“失去”所有权是否正确。在 Objective-C 中,您必须仔细整理您对对象的所有权。如果您创建或保留一个对象,您有责任释放它(直接或通过自动释放池)。但是,当您调用 release 时,您不会丢失对该对象的引用,如果其他东西保留了它,它仍将在内存中,并且您的指针仍可能指向它。

    你有一个指针 ffv,它只是一个指向某个内存的指针,你有一个在 ffv 指向的第一行中创建的对象。

    通过调用release,您声明您不再需要 ponter ffv 指向一个有效的对象,在这种情况下您会很高兴对象被释放。指针仍然指向那块内存,它仍然存在,因为它的保留计数通过将其分配给视图而增加。

    [ffv testMethod] 行有不工作的危险,因为它跟在release 后面,可能没有指向有效的对象。它之所以有效,是因为有其他东西让它保持活力。 ffv 仍然具有与第一次分配时相同的地址值。

    按顺序:

    1. 你的推论是正确的。
    2. 并非如此。
    3. 你不应该在release 调用之后使用ffv。您无法保证该对象会在您身边。

    这些是我们在这里使用的指针,而不是您在 Java 或 C# 中找到的引用。您必须编组您对对象的所有权,创建它,拥有一些指向它的指针,并通过仔细管理保留和释放调用,您可以在需要时将其保存在内存中。

    【讨论】:

    • “你一开始并不拥有任何东西,你只是有一个指针并要求保留然后释放该对象”。实际上,您拥有您创建的任何对象(例如通过alloc-init),直到您放弃对它的所有权(通过releaseautorelease)。事实上,考虑对象所有权而不是保留计数是一件好事。参考Memory Management Rules
    • 我试图解释上面的代码元素在做什么。 OP 似乎对 [ffv testMethod] 的工作原理感到困惑,因为他“放弃了所有权”。虽然在编码时跟踪谁对对象的引用总是有用的,而且我同意你应该认为自己是对象的所有者,如果你创建它,你应该释放或自动释放它——这不是我想要做的重点.我要说明的一点是该对象仍然存在,如果其他东西因为需要使用它而使其保持活动状态,则调用 release 不会执行任何操作。
    • 我理解您要表达的观点,但有些短语具有误导性,例如:“我不确定 '失去所有权' 是否正确。”。当您向对象发送释放消息时,您实际上是放弃了对它的所有权。
    • @albertamg - 无需大喊。我重写了最后一条评论,试图解释我想说的话。随意在答案中以自己的方式解释。
    • @albertang - 我正在尝试解决 Objective-c 编组您自己的内存“所有权”与 Java/C# 模型之间的区别,在 Java/C# 模型中,您声明自己对某个对象不感兴趣(说 myObj = null) - 你不能打电话给它。你是对的,你应该认为自己是所有者——但这并不是说该语言像其他对比语言那样强制执行所有权规则。
    猜你喜欢
    • 2011-05-11
    • 2011-03-13
    • 1970-01-01
    • 1970-01-01
    • 2015-12-09
    • 1970-01-01
    • 2010-11-14
    • 1970-01-01
    相关资源
    最近更新 更多