【问题标题】:Difference between creating a local variable and assigning to ivar and directly assigning to ivar?创建局部变量和分配给 ivar 和直接分配给 ivar 的区别?
【发布时间】:2011-01-13 17:34:39
【问题描述】:

我一直想知道为什么所有的苹果代码示例都使用这样的代码:

UINavigationController *aNavigationController = [[UINavigationController alloc]
          initWithRootViewController:rootViewController];

self.navigationController = aNavigationController;

[self.view addSubview:[navigationController view]];

[aNavigationController release];

他们总是创建一个局部变量并将其分配给 ivar 为什么不干脆这样做:

self.navigationController = [[UINavigationController alloc]
          initWithRootViewController:rootViewController];;

[self.view addSubview:[navigationController view]];

[navigationController release];

除了更容易理解之外,还有其他原因吗?这是最佳做法吗?

-奥斯卡

【问题讨论】:

    标签: iphone variables instance variable-assignment local


    【解决方案1】:

    您的替换代码不正确,因此说明了 Apple 试图阻止的问题。这是您的代码:

    self.navigationController = [[UINavigationController alloc]
          initWithRootViewController:rootViewController];
    
    [self.view addSubview:[navigationController view]];
    
    [navigationController release];
    

    你遗漏了“自我”。在您的参考文献中。也许您打算直接访问 ivar,但在这种情况下,您通过混合访问器和直接 ivar 访问创建了非常混乱的代码(并且通过在访问器外部使用直接 ivar 访问违反了基本规则)。如果不是,那么你的意思是这样写:

    self.navigationController = [[UINavigationController alloc]
          initWithRootViewController:rootViewController];
    
    [self.view addSubview:[self.navigationController view]];
    
    [self.navigationController release];
    

    最后一行是非常错误的。永远不要将 -release 发送到方法调用的结果。所以不,你这样做的方式不正确。

    也就是说,Apple 和我在如何做到这一点上存在分歧。这是我的做法:

    self.navigationController = [[[UINavigationController alloc]
          initWithRootViewController:rootViewController] autorelease;
    
    [self.view addSubview:[self.navigationController view]];
    

    我喜欢 -autorelease 因为我发现它可以防止错误。分配和释放获取的距离越远,开发人员注入内存泄漏的可能性就越大(例如通过添加“返回”)。自动释放通过将保留和释放保持在一起来避免这种情况,使将其用作临时变量的意图更加清晰,并且通常使代码审查更加容易。

    Apple 在他们的示例代码中倾向于不同意我的观点,因为他们通过使用发布而不是自动发布来强调性能。我发现这是错误的优化,因为在任何情况下都不会在此运行循环期间释放该对象(因此不会节省内存),并且我相信自动释放的非常小的性能损失可以弥补减少因错误使用释放而导致的内存泄漏。

    autorelease 与 release 的争论充满了灰色阴影(我当然直接在循环中使用 release),并且不同的开发人员有不同的方法,但无论哪种情况,您的替换代码都不是正确的方法。

    【讨论】:

    • 谢谢你的好回答。我唯一不清楚的是为什么我永远不应该向访问器调用的结果发送释放?
    • 你永远不应该释放方法调用的结果。访问器只是方法调用的特定实例。你应该只发布你保留的东西。例如,您假设您从 -foo 返回的对象与您传递给 setFoo: 的对象相同。这可能不是真的(缓存或复制可能会改变这一点)你不应该依赖访问器的内部实现细节;您可以稍后更改它们。只需遵循三个神奇的词,内存管理就不会成为问题。 robnapier.net/blog/three-magic-words-6
    【解决方案2】:

    第一行的区别在于 Apple 的版本将对象创建和分配给 ivar 分开,而您的版本将两者放在一起。从概念上讲,Apple 的版本更容易理解。据我所知,这不是最佳实践。

    【讨论】:

      【解决方案3】:

      两个版本都没有检查 nil 值:

      (假设 self.navigationController 是一个保留其值的属性)

      self.navigationController = [[UINavigationController alloc]
          initWithRootViewController:rootViewController];
      if (self.navigationController != nil) {
          [self.view addSubview: navigationController.view;
          [self.navigationController release];
      }
      

      你可以说这是一种风格,但在我看来,它可以减少错误代码。

      【讨论】:

        【解决方案4】:

        可能和上面的例子一样,但也有可能不是。

        记住

        self.navigationController = aNavigationController;
        

        相同
        [self setNavigationController:aNavigationController];
        

        你不知道setNavigationController 方法内部发生了什么。它可能正在初始化一个不同的对象并将其设置为 iVar,然后您将其释放,从而导致崩溃。

        【讨论】:

        • @OscarMK,第二个代码位适用于基于保留的属性,但不适用于 kubi 提及的基于复制的属性。
        • 我知道它们是相同的,但是如果您使用的是 synthesize 而不是覆盖 setNavigationController ,是否不能保证为它分配您正在粘贴的值并将其发送保留消息? (假设您使用保留声明了您的财产)。此外,这两种方法都使用相同的 self.navigationController,所以我不确定这与问题有什么关系?
        • 如果属性是retain,我想不出任何方式会导致问题,但如果是copyassign,它肯定会导致问题。我不会这样写,我猜它总有一天会回来咬你,没有理由不按照你列出的第一种方式来写。
        【解决方案5】:

        因为很明显代码正在使用 UINavigationController 实例变量。那么就没有理由这样做了:

        self.navigationController = aNavigationController
        

        如果你不保留它。

        但是,如果你这样做:

         self.navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
        

        之后,如果你这样释放它:

        [navigationController release];
        

        看起来我们正在释放实例变量,该实例变量应该在初始化导航控制器的当前类的生命周期内保留。所以,很容易出错,让初学者认为应该只在dealloc方法中释放。

        这两种方法最终都将保留计数为 0。如果在 dealloc 实现中:

        [navigationController release]; // 1 for the ivar
        [super dealloc]; // 0 for the retained subviews
        

        【讨论】:

        • 两个分配(alloc、retain 和 addsubview)的保留计数均为 3,也因为保留计数为 3,当您调用 release 时它仍然有 2,因此即使从视图中删除它它仍然有 1,然后你在 dealloc 处释放它。
        • Rob 的回答很清楚。但是,对于那些不注意保留计数的人来说,与您的代码相比,Apple 的示例代码更容易理解。对于初学者来说,这是违反直觉的,如果 navigationController 被释放了两次(在那里和在 dealloc 中)。他们都有相同的结果。保留计数将是平衡的。但是,示例代码更清楚地表明您正在对同一个变量执行分配/初始化和释放。然后,在代码的其他部分处理实例变量内存管理。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-07
        • 2019-03-20
        • 2021-10-27
        • 1970-01-01
        • 1970-01-01
        • 2019-02-28
        相关资源
        最近更新 更多