【问题标题】:Why should I write [anView release], anView = nil; rather than [anView release];?为什么要写[anView release], anView = nil;而不是 [anView 发布];?
【发布时间】:2010-10-22 19:09:34
【问题描述】:

我在某处读到 - 关于内存不足警告并放弃所有子视图的不可见视图(= 整个笔尖,我认为),您应该这样做:

-(void)dealloc {
    [anView release], anView = nil;
    [someImageView release], someImageView = nil;

    [super dealloc];
}

而不是

-(void)dealloc {
    [anView release];
    [someImageView release];

    [super dealloc];
}

在我调用 release 之后,将这些指针设为 nil(=“无对象”)的原因是什么?让我猜猜:由于某种原因,其他一些方法可能会保留视图(任何人都可以举例说明何时会发生这种情况?),然后发生 didReceiveMemoryWarning 事情,并且您释放了当前不可见的整个 nib+view(即在多视图应用程序中)。一旦用户想再次查看该视图,您将再次快速加载 nib,然后:它加载所有视图,连接插座,然后 BANG!您的其他保留视图现在挂起,没有任何指针在内存砖的某个地方孤独,导致严重的内存泄漏,直到您的应用程序崩溃。

对/错?

【问题讨论】:

    标签: iphone cocoa-touch memory-management uikit


    【解决方案1】:

    原理比UIView更通用。确实它比 Objective-C/Cocoa -release 方法更通用。它也适用于 C malloc()/free() 内存函数。

    当您不再需要某个对象或任何内存区域时,首先释放/释放它。然后,为了确保您不会再次使用它,您可以通过将nil 分配给对象或将NULL 分配给内存指针来清除访问此对象或内存区域的方法。

    【讨论】:

    • +1。要点是取消引用空指针会立即中止执行,而推迟指向已释放内存的指针可能会“工作”一段时间并导致令人不快的错误。
    • 谢谢。我想知道为什么我们在代码示例中很少看到这种模式。我从未在 Apple 的代码中见过它。但对我来说这样做听起来很合理,而且我想无论如何也不会受到伤害。
    • 示例代码很少遵循最佳实践。大多数示例代码的目标是尽可能简洁地展示一个原理、API 或方法。
    • 除了向 nil 发送消息是完全可以的,所以你不会收到错误。分配 nil 没有帮助。
    • 有时,您可以检查是否已使用if (obj==nil) [[obj alloc] init]; 创建了对象。在这种情况下,分配nil 很有用。
    【解决方案2】:

    由于某种原因,其他一些方法可能会保留视图

    除非您自己调用dealloc,否则只有在保留计数变为零时才会调用它。

    请注意,在 Objective-C 中,向nil“对象”发送消息(通常)非常好。这样做不会使您的程序停止,但该消息会被忽略。但是,您不能向已释放的对象发送消息,这会导致崩溃。

    因此,以下内容会给您一个错误:

    [anView release];
    [anView doSomething];
    

    但是,这其实没问题:

    [anView release];
    anView = nil;
    [anView doSomething];
    

    这是一个口味问题,但对于上述情况,您实际上可能更愿意让您的程序崩溃,而不是想知道为什么 doSomething 没有执行...

    另请参见 Apple 的 Introduction to The Objective-C 2.0 Programming Language 中的 Sending Messages to nil

    【讨论】:

      【解决方案3】:

      释放对象时调用-dealloc 方法,之后不会执行对象上的其他方法。因此,将任何实例变量设置为 nil 在该对象之外都没有影响。

      如果您在类中的其他位置释放对象(不使用 setter),则将实例变量设置为 nil 以防止其他位置的代码向该地址发送消息非常重要。

      【讨论】:

      • “之后不会执行对象上的其他方法” - +1 清楚地解释了显而易见的事情。谢谢! (至于“将实例变量设置为 nil 以防止其他地方的代码向该地址发送消息很重要”,我想知道为什么忽略意外消息比收到错误消息更可取......)
      • 因为所有程序都包含错误——即使是发布给客户的经过良好测试的程序。向 nil 发送消息(基本上是无操作)产生的错误类型通常不如向释放对象发送消息产生的错误严重。前者通常会导致程序无法完全按预期工作的逻辑错误,后者将导致崩溃。在开发过程中,欢迎使用崩溃,因为它们允许发现错误 - 对于已发布的产品而言并非如此。
      【解决方案4】:

      我经常使用这种模式:

      - (void) showHelp: (id) sender
      {
          if (helpController == nil)
          {
              helpController = [[HelpController alloc] initWithNibName: @"Help" bundle: [NSBundle mainBundle]];
          }
          [self presentModalViewController: helpController animated: YES];    
      }
      - (void)didReceiveMemoryWarning {
          [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
          // Release anything that's not essential, such as cached data
          [helpController release];
          helpController = nil;
      }
      

      几乎所有我分配的视图控制器都是模态的,或者是“临时的”。这样,如果我再次需要它,它就会挂起,但如果内存不足,它就会消失。

      【讨论】:

        【解决方案5】:

        如果您的访问器具有与它们关联的属性 yoc 并作为更简洁的方法执行以下操作,而不是执行显式释放并将其设置为 nil:

        - (void) dealloc
        {
            self.retainedProperty1 = nil;
            self.retainedProperty2 = nil;
            self.copiedProperty = nil;
            self.assignedProperty = nil;
        }
        

        通过这种方式,您可以获得更少重复的代码,因为合成代码会为您处理发布。

        编辑:我应该指出你的属性不能是只读的,否则你会因为明显的原因得到编译器错误:)

        【讨论】:

        • 没有。这是苹果明确禁止的,并且在某些情况下是导致崩溃的好方法。见stackoverflow.com/questions/192721/…
        • 该链接提出了一个很好的观点,但是如果您只将它用于完全合成的读写属性,这仍然是一个坏主意吗? (这意味着您没有为属性声明 synthesize 并且仍然覆盖 getter 或 setter)
        • 现在可能没问题,具有完全合成的属性。但是 Apple 不保证它被允许并建议不要这样做,因此这可能会在未来的任何 SDK 中中断。
        猜你喜欢
        • 1970-01-01
        • 2015-11-12
        • 1970-01-01
        • 1970-01-01
        • 2011-08-17
        • 1970-01-01
        • 2011-12-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多