【问题标题】:How is retain setter implemented with @synthesize?使用@synthesize 如何实现retain setter?
【发布时间】:2011-04-24 21:06:09
【问题描述】:

我在标题中有以下内容:

@property (nonatomic, retain) UIView *overlay;

在实现中:

@synthesize overlay;

然后:

UIView *tempOverlay = [[UIView alloc] initWithFrame:CGRectMake(160.0f, 70.0f, 150.0f, 310.0f)];
self.overlay = tempOverlay;
[tempOverlay release];

上面的tempOverlay变量不是没必要吗?我不能这样做吗:

self.overlay = [[UIView alloc] initWithFrame:CGRectMake(160.0f, 70.0f, 150.0f, 310.0f)];

【问题讨论】:

    标签: iphone objective-c memory-management properties retain


    【解决方案1】:

    合成的保留设置器看起来像:

    - (void)setValue: (id)newValue
    {
        if (value != newValue)
        {
            [value release];
            value = newValue;
            [value retain];
        }
    }
    

    在你的情况下,你有两种有效的方法:

    1) 创建一个临时变量,alloc/init(= 保留),设置为属性,释放。

    IView *tempOverlay = [[UIView alloc] initWithFrame:CGRectMake(160.0f, 70.0f, 150.0f, 310.0f)];
    self.overlay = tempOverlay;
    [tempOverlay release];
    

    2) 没有 temp var,直接设置为 ivar。

    overlay = [[UIView alloc] initWithFrame:CGRectMake(160.0f, 70.0f, 150.0f, 310.0f)];
    

    更新: 如果使用方法 2),则必须显式处理其余的内存管理(不仅仅是保留),如果需要,释放它之前可能具有的任何先前值。如果只在init 中完成一次(例如),您可以在dealloc 中添加一个[overlay release];

    【讨论】:

    • 如果合成的保留设置器看起来像你有它,那么第一次设置值时会发生什么?如果您尝试先释放旧值,它会不会因 BAD ACCESS 而崩溃?或者,是否所有属性都自动赋予初始保留计数 1?
    • 是否应该使用方法 2),也许是第一次设置?但是,如果您在设置它之后设置它,那么旧值不会被释放(除非明确完成)。那么,这会导致内存泄漏,对吗?
    • 最初,value 是 nil,发送任何消息到 nil(包括保留/释放)都是一个 no op 返回 nil。
    • 对于方法 2,是的,您显然需要在某个地方释放您的 ivar。
    • 如果你正在实现一个自定义的setter,你是否也应该考虑KVO?
    【解决方案2】:

    使用retain 属性指定应在新对象上调用retain,并将先前的值发送到release

    因此,在您的第二个代码块中,对象的保留计数将变为 2,因为您不再释放它,而 setter 正在保留它。这不太可能是您想要的。

    【讨论】:

      【解决方案3】:

      如果将对象直接分配给属性,仍然必须释放它:

      self.overlay = [[[UIView alloc] initWithFrame:CGRectMake(160.0f, 70.0f, 150.0f, 310.0f)] autorelease];
      

      【讨论】:

      • 抱歉,我没有向右滚动查看autorelease。这是一个正确的解决方案,但我想在设置overlay后显式释放临时值。
      【解决方案4】:

      由于您的属性是使用(保留)定义的,因此您使用合成设置器(通过 self.overlay 语法)设置的任何实例都将自动发送保留消息:

      // You're alloc'ing and init'ing an object instance, which returns an 
      // instance with a retainCount of 1.
      UIView *tempOverlay = [[UIView alloc] initWithFrame:CGRectMake(160.0f, 70.0f, 150.0f, 310.0f)];
      
      // The overlay property is defined with (retain), so when you assign the new 
      // instance to this property, it'll automatically invoke the synthesized setter, 
      // which will send it a retain message. Your instance now has a retain count of 2.
      self.overlay = tempOverlay;
      
      // Send a release message, dropping the retain count to 1.
      [tempOverlay release];
      

      如果你这样做:

      self.overlay = [[UIView alloc] initWithFrame:CGRectMake(160.0f, 70.0f, 150.0f, 310.0f)];
      

      您的叠加层的保留计数为 2,这可能会在您的应用程序中的某个时间点导致泄漏。

      【讨论】:

        【解决方案5】:

        感谢您的所有回答。我收到了一些相互矛盾的声明,因此我在 UITableViewController 中测试了以下内容:

        - (id)initWithStyle:(UITableViewStyle)style {
            if ((self = [super initWithStyle:style])) {
                NSLog(@"count: %d", [overlay retainCount]);
                self.overlay = [[UIView alloc] initWithFrame:CGRectMake(160.0f, 70.0f, 150.0f, 310.0f)];
                NSLog(@"count: %d", [overlay retainCount]);
                [overlay release]; NSLog(@"released once");
                NSLog(@"count: %d", [overlay retainCount]);     
                [overlay release]; NSLog(@"released twice");
                NSLog(@"count: %d", [overlay retainCount]);
            }
            return self;
        }
        

        我得到以下控制台输出:

        • 有时它运行良好:

          count: 0
          count: 2
          released once
          count: 1
          released twice
          count: 1
          
        • 其他时候它崩溃了:

          count: 0
          count: 2
          released once
          count: 1
          released twice
          Program received signal:  “EXC_BAD_ACCESS”.
          

        我知道使用tempOverlay 的方法是正确的。它看起来很麻烦,但我更喜欢它而不是autorelease,因为我不明白autorelease 是如何工作的或何时调用它。有一件事情是肯定的。上面的代码是错误的,因为我不希望 overlay 的保留计数为 2。

        奇怪的是我不能释放它两次。即使它没有崩溃,保留计数也不会减少。

        不管怎样,我想我现在会坚持使用tempOverlay

        【讨论】:

        • 保留计数为 2 在您拥有的代码中是正确的。一次用于 iVar 保留,再一次用于“alloc”调用。
        【解决方案6】:

        可以,可以直接将新创建的对象赋值给overlay对象。如果你愿意,你可以通过打印出对象的保留计数来证明给自己看

        NSLog(@"count: %d", [overlay retainCount]);
        

        【讨论】:

        • -1 你没抓住重点。该对象是使用 alloc 创建的。因此,您拥有它,并且必须在完成后释放它,在这种情况下您是立即的。
        • 我不这么认为。看看我给出的答案。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-05-24
        • 1970-01-01
        • 2011-11-21
        • 1970-01-01
        相关资源
        最近更新 更多