私人合成财产是矛盾吗?

时间:2011-04-27 17:03:04

标签: iphone objective-c oop encapsulation

在浏览了初学者的iPhone开发人员书并在线阅读示例代码之后,我注意到大多数Objective C程序员几乎合成了每个实例变量。有些变量很方便snythesize,但大多数变量不应该遵循面向对象的封装原则。最糟糕的是标记为私有的合成属性。试图使用其他人的代码的C ++程序员将读取头文件中的公共字段和方法。他们将跳过私有变量。这个C ++程序员不会知道你打算以某种有意义的方式使用私有属性。

在Apple提供的lazy table image loading上查看此示例模板:

标题

@interface ParseOperation : NSOperation <NSXMLParserDelegate>

{
@private
    id <ParseOperationDelegate> delegate;
    NSData          *dataToParse;
    NSMutableArray  *workingArray;
    AppRecord       *workingEntry;
    NSMutableString *workingPropertyString;
    NSArray         *elementsToParse;
    BOOL            storingCharacterData;
}

来源

@interface ParseOperation ()
@property (nonatomic, assign) id <ParseOperationDelegate> delegate;
@property (nonatomic, retain) NSData *dataToParse;
@property (nonatomic, retain) NSMutableArray *workingArray;
@property (nonatomic, retain) AppRecord *workingEntry;
@property (nonatomic, retain) NSMutableString *workingPropertyString;
@property (nonatomic, retain) NSArray *elementsToParse;
@property (nonatomic, assign) BOOL storingCharacterData;
@end

@implementation ParseOperation
@synthesize delegate, dataToParse, workingArray, workingEntry, workingPropertyString, elementsToParse, storingCharacterData;

现在我知道这不是C ++,我们不应该假设所有的C ++实践都应该在Objective C中得到尊重。但是Objective C应该有充分的理由偏离一般的编程实践。

  1. 为什么所有私有ivars合成?当您将项目视为一个整体时,外部类只使用NSMutableArray *workingArray。所以其他的ivars都没有塞特尔和吸气剂。
  2. 为什么非常敏感的ivars合成?首先,现在id delegate有一个setter,这个对象的用户可以在XML解析的中间切换委托,这是没有意义的。此外,NSData *dataToParse是从网络检索的原始XML数据。现在它有一个setter,这个对象的用户可以破坏数据。
  3. 标题中标记所有内容private的重点是什么?由于所有的伊娃都被合成为具有吸气剂/固定剂,因此它们实际上是公开的。你可以将它们设置为你想要的任何东西,你可以随时获得它们的价值。

2 个答案:

答案 0 :(得分:10)

我遵循这个例子在我的许多课程中建模的习语,所以我可以尝试解释我自己的理由。

此示例中的属性在.m文件的类扩展中声明。这使它们实际上是私密的。任何从另一个类访问这些属性的尝试都会在编译时导致“找不到属性”错误。

对于来自其他语言的开发人员,为私有实例变量合成getter和setter似乎很奇怪。实际上,我这样做只有一个原因。一致使用时,合成属性可以简化内存管理,并有助于避免可能导致错误的粗心错误。以下是几个例子:

考虑一下:

self.workingPropertyString = [NSMutableString string];

与此相对:

workingPropertyString = [[NSMutableString string] retain];

许多开发人员声称这两项任务在功能上是等同的,但是有一个重要的区别。如果workingPropertyString已经指向保留的对象,则第二个赋值会泄漏内存。要编写功能上等同于合成setter的代码,你必须做这样的事情:

NSMutableString *newString = [NSMutableString string];
if (workingPropertyString != newString) {
    [workingPropertyString release];
    workingPropertyString = [newString retain];
}

此代码避免泄漏实例变量可能指向的任何现有对象,并且它可以安全地处理您可能将同一对象重新分配给实例变量的可能性。合成的二传手为你做了所有这些。

当然,我们可以看到(workingPropertyString != newString)在这种情况下总是如此,所以我们可以简化这个特定的赋值。事实上,在大多数情况下,您可以通过简单的直接赋值实例变量来逃避,但当然这是特殊情况,往往会产生最多的错误。我更喜欢安全地播放它并通过合成的setter设置我的所有对象实例变量。我的所有实例对象分配都是简单的单行,如下所示:

self.foo = [Foo fooWithTitle:@"The Foo"];

或者这个:

self.foo = [[[Foo alloc] initWithTitle:@"The Foo"] autorelease];

这种简单性和一致性使我的虚弱大脑更少考虑。因此,我几乎从未遇到过与内存管理相关的错误。 (我知道autorelease成语理论上可以在紧密的循环中消耗过多的内存,但我在实践中还没有遇到过这个问题。如果我这样做,那么优化就是一个简单的例子。)

我喜欢这种做法的另一件事是我的dealloc方法都是这样的:

- (void)dealloc {
    self.delegate = nil;
    self.dataToParse = nil;
    self.workingArray = nil;
    self.workingEntry = nil;
    self.workingPropertyString = nil;
    self.elementsToParse = nil;
    [super dealloc];
}

编辑:丹尼尔迪克森指出了一些 在dealloc中使用访问器的风险 我没有考虑过。见 评价。

其中每个对象属性都设置为nil。这会同时释放每个保留属性,同时将其设置为nil,以避免由于EXC_BAD_ACCESS导致的某些崩溃。

请注意,即使该属性声明为self.delegate = nil;,我也设置了(nonatomic, assign)。这项任务并非绝对必要。事实上,我可以完全取消(nonatomic, assign)个对象的属性,但我再次发现在所有实例变量中一致地应用这个习惯用法让我的大脑更少考虑,并进一步减少了我的机会通过一些粗心的错误来创造一个bug。如有必要,我可以简单地将属性从(nonatomic, assign)翻转到(nonatomic, retain),而无需触及任何内存管理代码。我喜欢那样。

也可以使用一致性作为合成私有标量变量属性的参数,正如您在BOOL storingCharacterData;的情况下所做的那样。这种做法可确保每个实例变量赋值看起来都像self.foo = bar;。我自己通常不打算创建私有标量属性,但我可以看到这种做法的一些理由。

答案 1 :(得分:0)

  

为什么所有的私人ivars   合成的?当你看着   仅作为整体的项目   使用NSMutableArray * workingArray   外面的课程。所以没有   其他伊娃应该有制定者和   吸气剂。

没有真正的需要;如果你打算直接访问所有的ivars,就不需要@synthesize。

  

为什么非常敏感的伊娃   合成的?一个,现在那个id   delegate有一个setter,用户   此对象可以切换委托   中间的XML解析,一些东西   这没有意义。另外,NSData   * dataToParse是从网络检索的原始XML数据。现在它有一个   setter,这个对象的用户可以   破坏了数据。

没有公开声明的setter / getter。如果该类的客户端想通过在中间切换委托来破坏事物,那么他们必须打破封装才能这样做。

所以,最终,一个非问题。

  

标记一切的重点是什么   在标题中私有?因为所有的伊娃   是合成的   getter / setters,他们是有效的   上市。你可以将它们设置为任何东西   你想要,你可以获得他们的价值   随时随地。

请注意,在该示例中甚至不需要声明ivars;编译器将根据@property声明自动合成它们。

传统上,@ private可以防止有人将ivar直接从外部转移到该类的实例。

请注意,anInstance-&gt; ivar或self-&gt; ivar几乎从不使用(并且,在使用时,它几乎总是出于错误的原因)。它有用处,但很少见。