【问题标题】:retain/release issues保留/释放问题
【发布时间】:2011-11-17 04:20:39
【问题描述】:

我刚刚分析了我的 iPhone 项目,对 XCode(4) 给出的结果感到非常困惑。例如,在我的一个视图控制器中,我有以下代码:

@property (nonatomic, retain) NSArray* menuItems;
@property (nonatomic, retain) NSArray* menuItemsOptions;

- (void)viewDidLoad 
{
   [super viewDidLoad];

   self.menuItems = [[NSArray alloc] initWithObjects:
                    NSLocalizedString(@"Foo", nil), 
                    NSLocalizedString(@"Bar", nil), 
                    nil];

  [self.menuItems release];

  self.menuItemsOptions = [[NSArray alloc] initWithObjects:
                           NSLocalizedString(@"More foo", nil), 
                           NSLocalizedString(@"more bar", nil), 
                           nil];

  [self.menuItemsOptions release];
...
}

menuItemsmenuItemsOptions 是带有 retain 选项的属性。如果我按下分析,XCode 将显示[self.menuItems release]; 行的错误:

http://i54.tinypic.com/2rqkfaf.png

让我更加困惑的是,XCode 不会显示 [self.menuItemsOptions release]; 行的错误

另一种方法的类似情况:

http://i55.tinypic.com/10hof9c.png

theSelectedBegintheSelectedEnd 又是带有保留选项的属性。

我发布此内容的原因是我的应用程序实际上会在第三方库中以非常神秘/无法理解的回溯崩溃,除非我添加了最后一张图片中看到的 copy不要 em> 添加release。添加release或省略copy会使应用程序再次崩溃,这就是我决定运行分析器的原因。

我做错了什么?

【问题讨论】:

  • 您可以在定义属性的地方发布标题代码吗?
  • 假设属性声明/实现是正确的,这听起来像其他事情很不对劲。请记住,分析器有时可能会出错。
  • 顺便说一下,如果属性是(意外地?)copy,分析器结果将是有意义的。
  • 没有看到您的属性定义或更多代码,我无法给出一个好的答案。您上面的代码看起来不错。

标签: xcode cocoa-touch memory-management retaincount


【解决方案1】:

尝试将someMethod 更改为:

-(void) someMethod:(NSDate*)fromDate toDate:(NSDate*)toDate
{
   if (editBegin)
   {
      NSDate *copiedDate = [fromDate copy];
      self.theSelectedBegin = copiedDate;
      [copiedDate release];
   }
   else
   {
      NSDate *copiedDate = [fromDate copy];
      self.theSelectedEnd = copiedDate;
      [copiedDate release];
   }
}

如果您对属性 theSelectedBegin 和 theSelectedEnd(我推荐)使用副本,例如:

@property (nonatomic, copy) NSDate *theSelectedBegin;
@property (nonatomic, copy) NSDate *theSelectedEnd;

下面的代码和上面的代码是等价的,但是更简洁干净。

-(void) someMethod:(NSDate*)fromDate toDate:(NSDate*)toDate
{
   if (editBegin)
   {
      self.theSelectedBegin = fromDate;
   }
   else
   {
      self.theSelectedEnd = fromDate;
   }
}

当您执行[myObj copy] 时,将返回一个新对象。执行[myObj retain] 会返回具有增加的保留计数的相同对象。如此有效,以下是糟糕的代码:

@property (nonatomic, copy) NSDate *myDate;
[...]

self.myDate = [someDate copy];
[self.myDate release];

拆开看起来更像……

@property (nonatomic, copy) NSDate *myDate;
[...]

NSDate *copyDate = [someDate copy];  // never gets released
self.myDate = copyDate;             // good so far for self.myDate
[self.myDate release];              // just released self.myDate (note: copyDate not released)

【讨论】:

    【解决方案2】:

    您从分析器收到警告的原因是 getter 方法不需要实际返回与您传递给 setter 的对象完全相同的对象。例如,想象以下代码:

    - (void)doSomethingWithAString:(NSString *)aString {
        self.myName = [[NSString alloc] initWithFormat:@"%@ the Great", aString];
        [self.myName release];
    }
    

    字符串是使用拥有方法 (-init...) 创建的,因此您拥有它。然后你把它交给myName 财产,它取得了所有权。现在您需要释放您从-init... 方法获得的所有权,这通过调用-release 来完成。太好了。

    上述代码的问题[self.myName release] 可能不会释放您传递给setter 的同一个对象。想象一下,如果 setter 是这样实现的:

    - (void)setMyName:(NSString *)someString {
        // Make sure to trim whitespace from my name!
        NSCharacterSet *whitespaceSet = [NSCharacterSet whitespaceCharacterSet];
        NSString *strippedString = [someString stringByTrimmingCharactersInSet:whitespaceCharacterSet];
    
        [myName autorelease];
        myName = [strippedString retain];
    }
    

    请注意,您传递给 setter 的对象不是存储到支持 ivar 的对象。当您调用[self.myName release] 时,您释放的是剥离的字符串,而不是原始字符串。原来的字符串现在泄露了,剥离出来的字符串已经过度释放了。

    简而言之,永远不要假设 getter 返回的对象与您传递给 setter 的对象相同

    【讨论】:

    • 谈论您的自定义设置器,我认为您是对的。但在我的情况下,我使用标准(保留)属性,它的设置器应该希望持有并保留我传递给它的相同对象。否则属性(尤其是保留选项)对我来说似乎毫无用处。
    • 真;您会认为标记为“保留”的属性是安全的。但是 setter 可能仍在进行一些验证,如果传递的对象由于某种原因无效,则将其设置为“nil”。编译器不能保证 setter 和 getter 将完全像合成版本一样工作——如果没有别的,子类可能已经覆盖了这些方法。无论如何,这就是分析仪警告您的原因。如果您觉得在这种特殊情况下很愚蠢,请随时向 Apple 提交错误。
    【解决方案3】:

    属性的一个吸引人的特性是属性访问器负责保留和释放它们指向的对象。我想不出一个人会明确保留或释放财产的情况。

    【讨论】:

    • 如果您将保留属性分配给您已经拥有的对象。
    • 如果分配给自动释放的对象或您管理的与属性分开的局部变量,您所说的是正确的。所以,self.myArray = [NSMutableArray array]; 没问题。但是,self.myArray = [[NSMutableArray alloc] init]; 需要 [self.myArray release] 来平衡保留计数。
    • 查看我对 typeoneerror 答案的评论。您在一行上分配的属性可能不会指向您在下一行释放的同一个对象。这甚至不仅仅是线程问题。如果属性的语义从保留更改为复制,您现在会泄漏并稍后崩溃。当然,setter 可以在子类中被覆盖,这可能会导致类似的问题。
    猜你喜欢
    • 2012-12-06
    • 1970-01-01
    • 2011-09-06
    • 1970-01-01
    • 2011-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多