为什么我不应该使用getter来释放objective-c中的属性?
一个StackOverflow用户告诉我,释放属性时不应该使用getter方法:
@property(nonatmic, retain) Type* variable; @synthesize variable; // wrong [self.variable release]; // right [variable release];
他没有详细解释为什么。 他们对我来说也一样。 我的iOS书上说,属性上的getter看起来像这样:
- (id)variable { return variable; }
那么这是不是意味着[self variable]
, self.variable
和variable
都是一样的?
一个典型的getter看起来更像这样:
- (id)variable { return [[variable retain] autorelease]; }
所以如果你使用[self.variable release]
你有一个额外的retain
和autorelease
,当你只想释放这个对象时,你并不需要这个额外的retain
和autorelease
,并且导致这个对象被释放的时间超过了必要的时间(当autorelease池被释放)。
通常情况下,你可以使用self.variable = nil
,它的好处是它也将variables设置nil
(避免由于悬挂指针引起的崩溃),或者是最快速的[variable release]
,在dealloc
方法中可能更合适如果你的setter有自定义的逻辑。
对于没有自定义存取器的保留属性,可以通过以下方式释放对象:
self.variable = nil;
这具有设置ivar(如果只有声明的属性,可能不会被称为“variables”)的效果,并释放之前的值。
正如其他人指出的,无论是直接释放伊娃(如果可用)或使用上述方法都是可以的 – 你不能做的就是调用释放从一个getter返回的variables。
您可以select编写自定义getter行为,这可能会导致完全不同的行为。 所以,你不能总是认为[variable release]
和[self.variable release]
具有相同的结果。
同样,你可以编写自定义的属性,而不需要独家的ivar支持它们…如果你开始从getters返回的引用释放对象,它可以快速地混乱!
可能还有其他原因,我不知道…
并非所有的获得者都采取这种forms:
- (id)variable { return variable; }
这只是最原始的forms。 属性本身应该build议更多的组合,这改变了实施。 上面的原始访问器不考虑与内存pipe理,primefaces性或复制语义结合使用的习惯用法。 在子类覆盖中实现也是脆弱的。
一些非常简短的例子, 在实现变得相当复杂的实际程序中事情显然变得更加复杂。
1)getter可能不会返回实例variables。 几种可能性之一:
- (NSObject *)a { return [[a copy] autorelease]; }
2)setter可能不保留实例variables。 几种可能性之一:
- (void)setA:(NSObject *)arg { ... a = [arg copy]; ... }
3)最终在整个程序中实施内存pipe理,这使得难以维护。 类的语义(以及它如何处理实例variables的引用计数)应保存在类中,并遵循预期结果的约定:
- (void)stuff:(NSString *)arg { const bool TheRightWay = false; if (TheRightWay) { NSMutableString * string = [arg mutableCopy]; [string appendString:@"2"]; self.a = string; [string release]; // - or - NSMutableString * string = [[arg mutableCopy] autorelase]; [string appendString:@"2"]; self.a = string; } else { NSMutableString * string = [arg mutableCopy]; [string appendString:@"2"]; self.a = string; [self.a release]; } }
不遵循这些简单的规则会使你的代码很难维护和debugging,并且很难扩展。
所以缺点是你想让你的程序易于维护。 直接在一个属性上调用释放需要你知道类的内部运作的很多上下文; 这显然是糟糕的,错过了很好的OOD理想。
它也期望作者/子类/客户能够准确地知道class级是如何背离约定的,当问题出现时,这是愚蠢和耗时的,当问题出现时(他们会在某个时刻),你必须重新学习所有的内部细节。
这些都是一个简单的例子,如何调用释放财产的结果引入问题。 许多现实世界的问题更加微妙和难以定位。