【问题标题】:Objective-C Setter Memory ManagementObjective-C Setter 内存管理
【发布时间】:2009-06-09 16:46:01
【问题描述】:

对 Objective-C 内存管理仍然有些困惑。我认为我的困惑源于自动释放的确切含义。

NSString *theBackendResponse = [[NSString alloc] initWithData:receivedData encoding:NSASCIIStringEncoding];
NSDictionary *accountDictionary = [theBackendResponse propertyList];
[viewController setAccountDictionary:accountDictionary];

现在,我应该如何处理视图控制器的 setAccountDictionary 方法中的 accountDictionary?现在我只是将实例变量“accountDictionary”设置为返回的任何内容。我应该将其设置为保留的,然后释放返回的吗?考虑到 NSString 的 propertyList 方法是自动释放的,我的 setter 代码块应该是什么样子?

顺便说一句,如果我释放了BackendResponse,我会丢失accountDictionary吗?我想不会……

【问题讨论】:

    标签: iphone objective-c memory-management setter


    【解决方案1】:

    调用[objectInstance autorelease] 会将对象添加到当前NSAutoreleasePool。当该池收到drain 消息时,它会向池中的所有对象发送release。如果这些对象中的任何一个的 retainCount 达到 0,它们就会在此时被释放。 autorelease 的目的是允许您标记要在“未来某个时间”释放的对象。这对于返回一个新分配的对象但想要释放它的方法特别有用,这样调用者就不必拥有返回的对象的所有权。方法可能如下所示:

    - (id)myMethod {
        id myObj = [[SomeClass alloc] init];
    
        ...
    
        return [myObj autorelease];
    }
    

    myMethod 的调用者如果想要获得返回值的所有权,则将 retain 返回值,否则忽略它。当当前NSAutoreleasePool 耗尽时,myObj 将收到一条释放消息。如果没有其他对象拥有它(即已向其发送retain 消息),它将被释放。

    所有这些都在 Cocoa Memory Management Programming Guide 中进行了解释。即使你已经读过它,它总是值得再读一遍。

    所以,回答你的问题:

    首先,您应该释放theBackendResponse。如果不这样做,您将泄漏内存。您不需要知道accountDictionary 对字符串做了什么:如果它需要保留引用,它将保留theBackendResponse。您拥有theBackendResponse 的所有权,因为您alloc 拥有它,因此您必须放弃该所有权(通过release 或间接通过autorelease)。

    其次,如果您想分别保留对该对象或值的引用,则必须保留或复制参数到setAccountDictionary:。标准的 setter 方法看起来像这样(假设您不需要原子语义):

    -(void)setAccountDictionary:(NSDictionary*)newDict {
      if(newDict != accountDictionary) {
        id tmp = accountDictionary;
        accountDictionary = [newDict copy]; //Since newDict may be mutable, we make a copy so that accountDictionary isn't mutated behind our back.
        [tmp release];
      }
    }
    

    你还必须记得在dealloc方法中releaseaccountDictionary:

    - (void)dealloc {
        [accountDictionary release];
        [super dealloc];
    }
    

    由于您似乎在使用 NSViewController,我假设您使用的是 Leopard (OS X 10.5),在这种情况下,您可能应该使用 @property@synthesized getter/setter(如果可能)。为此,请添加一个

    @property (copy,readwrite) NSDictionary * accountDictionary; 
    

    @interface 类声明。并在您的控制器类的@implementation 块中添加@synthesize accountDictionary; 指令。

    【讨论】:

    • 太棒了,谢谢。现在我明白了。我实际上在 iPhone 上,使用 UIViewControllers,但显然 Objective-C 2.0 仍然适用。我不会为此使用合成的 getter/setter,但我会在未来使用。再次感谢!
    • 这有点不正确,您应该永远在保留/复制新对象之前释放旧对象。如果accountDictionary 以某种方式引用newDict,则首先调用release 会过早释放它。鉴于我无法添加多行代码,我将添加一个替代解决方案作为答案
    【解决方案2】:

    一般来说,一个对象或方法不必关心另一个对象或方法如何管理内存。其他人自动发布某些东西的事实与您无关。 所有权的概念比较简单。所以retain 和其他一些方法声称拥有所有权,而releaseautorelease 放弃它。如果一个对象需要保留对另一个对象的引用,它应该在需要时声明所有权。因此,setter 方法通常要么保留或复制新值,然后释放或自动释放旧值。

    我强烈推荐阅读the Cocoa memory management guidelines。它们并不长也不复杂,理解它们非常重要。

    【讨论】:

      【解决方案3】:

      在旧值是唯一拥有新值的对象的情况下,在释放旧值之前,set 访问器方法应该始终 copy / retain 传入值:

      -(void)setAccountDictionary:(NSDictionary*)newDict {
          id old = accountDictionary;
          accountDictionary = [newDict copy];
          [old release];
      }
      

      如果accountDictionary 引用newDict 并且newDict 的保留计数为1,则在调用[newDict copy] 之前调用[accountDictionary release] 将导致保留计数变为0,因此释放@987654329 @。

      作为错误代码的示例,我们释放旧字典然后复制新字典:

      -(void)setAccountDictionary:(NSDictionary*)newDict {
          [accountDictionary release];
          accountDictionary = [newDict copy];
      }
      

      并具有以下代码:

      NSDictionary *dict = [obj accountDictionary];
      [obj setAccountDictionary:dict];
      

      这是人为的,但它表明在 setter 中,accountDictionarynewDict 指的是同一个实例。如果保留计数为 1,[accountDictionary release] 行会将保留计数减少到 0,从而从内存中释放实例。 [newDict copy] 现在将引用无效实例。

      【讨论】:

      • +1 同意。你能举一个可能发生这种情况的例子吗?
      【解决方案4】:

      Apple 在实现访问器时描述了几个概念:Memory Management Accessor Methods
      如果您可以使用 Objective-C 2.0,我会使用属性和点语法。 属性是 Objective-C 2.0 中的新特性,并提供自动访问器生成。
      在 .h 文件中:

      @property (retain) NSDictionary* accountDictionary;
      

      在实现中:

      @synthesize accountDictionary;
      

      Synthesize 为您的 NSDictionary 生成访问器方法。 (如果你想提供自己的实现,你也可以这样做)

      【讨论】:

      • 我可以这样做,但这并不能真正帮助我理解后台发生的事情:)
      猜你喜欢
      • 1970-01-01
      • 2020-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-08
      相关资源
      最近更新 更多