2011-04-27 68 views
8

在阅读初学者的iPhone开发者书籍并在线阅读示例代码之后,我注意到大多数Objective C程序员几乎综合了所有实例变量。一些变量可以方便地进行优化,但是在遵守面向对象的封装原则时,大多数变量不应该这样做。最差的是合成属性标记为私人。试图使用别人代码的C++程序员将读取头文件中的公共字段和方法。他们将跳过私有变量。此C++程序员不会知道您希望以某种有意义的方式使用私有属性。是一个私人综合财产矛盾吗?

看看苹果提供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++和我们不应该假定在Objective C中应该遵守所有的C++实践目标C应该有充分的理由偏离一般编程实践。

  1. 为什么全部是私人ivars合成?从整个项目来看,外部课程只使用NSMutableArray *workingArray。所以其他的ivars都不应该有setter和getters。
  2. 为什么非常敏感的ivars合成?首先,现在id delegate有一个setter,这个对象的用户可以在XML解析中间切换委托,这是没有意义的。另外,NSData *dataToParse是从网络检索到的原始XML数据。既然它有一个setter,这个对象的用户可能会破坏数据。
  3. 什么是标志着包头一切private的地步?因为所有的ivars都被合成为具有getter/setter,所以它们是公开的。你可以将它们设置为任何你想要的,并且你可以随时获得它们的价值。
+0

昨天有一些类似的问题:[公开阅读,私人保留](http://stackoverflow.com/questions/5788458/public-read-private-retain-property); [私人@property创建一个私人ivar?](http://stackoverflow.com/questions/5784875/does-a-private-property-create-an-private-instance-variable)我不确定是否他们虽然回答你的问题。 – 2011-04-27 17:10:21

+0

您可能也有兴趣:[iOS:必须每个iVar真的是属性?](http://stackoverflow.com/questions/5031230/ios-must-every-ivar-really-be-property) – 2011-04-27 19:56:38

回答

10

我遵循这个例子在我的很多课程中模仿的习惯用法,所以我可以试着解释我自己为这种做法的理由。

本示例中的属性在.m文件的类扩展名中声明。这使他们有效的私人。任何试图从另一个类访问这些属性的尝试都会在编译时导致“Property not found”错误。

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

考虑一下:

self.workingPropertyString = [NSMutableString string]; 

与此:

workingPropertyString = [[NSMutableString string] retain]; 

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

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

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

当然我们可以看到(workingPropertyString != newString)在这种情况下总是成立,所以我们可以简化这个特定的任务。事实上,在大多数情况下,你可能会简单地直接赋值给一个实例变量,但当然这是例外情况往往会造成最大的错误。我更喜欢玩它安全,并通过合成设置器来设置我所有的对象实例变量。我所有的实例对象分配是简单的单行看起来像这样:

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]; 
} 

编辑:丹尼尔Dickison指出了一些 风险使用的dealloc 存取,我还没有考虑。请参阅 评论。

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

请注意,即使该属性被声明为(nonatomic, assign),我也设置了self.delegate = nil;。这项任务不是绝对必要的。实际上,我可以完全不用我的(nonatomic, assign)对象的属性,但是我又发现,在所有实例变量中一致地应用此惯用法使我的大脑更少思考,并进一步减少了创建错误通过一些粗心的错误。如果有必要,我可以简单地将(nonatomic, assign)的属性翻转到(nonatomic, retain)而不必触摸任何内存管理代码。我喜欢。

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

+5

不错的答案,但一个挑剔:设置属性为零,而不是直接在'dealloc'方法中调用'release'是不鼓励的(有些苹果文档说,尽管我现在找不到它)。原因是setter可以发出KVO通知,导致观察者尝试访问部分释放的实例。 – 2011-04-27 19:54:55

+0

Ewww。好点子。我没有用KVO做过多的工作,所以我没有遇到过这种情况,但这很有道理。我将不得不寻找这些苹果文档,并可能重新考虑我如何编写我的deallocs。感谢您让我看到这种风险。 – cduhn 2011-04-27 20:06:42

+0

在这一点上,Apple的文档似乎处于不一致的状态。 Objective C Programming Language文档说,如果你在现代运行时使用合成实例变量,“你必须调用访问器方法”,因为你不能直接访问实例变量。然而,似乎在最新的工具中,你可以直接访问伊娃,所以我想我在dealloc中使用的惯用法是'[伊娃尔释放],伊娃=零;'。这里有一个很好的讨论:http://stackoverflow.com/questions/1283419/valid-use-of-accessors-in-init-and-dealloc-methods – cduhn 2011-04-27 20:54:07

0

为什么所有私人ivars 合成?当你看 项目作为一个整体,只有 的NSMutableArray * workingArray由外类使用 。所以 其他ivars都不应该有setter和 获得者。

没有真正的需要;如果您要直接访问所有ivars,则不需要@synthesize。

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

没有公开声明setter/getters。如果班上的一位客户想要通过在中间切换委托来破坏事情,那么他们必须打破封装才能做到这一点。

因此,最终是一个非问题。

什么意思在标题中标记一切 私人?由于所有ivars 都合成为具有 获取者/设置者,因此它们实际上是公开的 。你可以将它们设置为你想要的任何东西 ,你可以随时得到它们的值 。

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

传统上,@私人保护免受有人直接从外部将伊娃从该类别的一个实例中剔除。

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

+0

似乎喜欢Objective C应该通过代码布局的方式*强制*封装而不是*建议*封装。在阅读整个项目之后,我可以看到苹果程序员*建议我不要篡改私有ivars,但我不应该那样做。我应该可以只读头文件来理解如何使用这个类,并让编译器*强制封装。 – JoJo 2011-04-27 18:10:12

+0

如果执行封装,'anInstance-> publicIVar'和'self-> trulyPrivateIvar'有什么问题? – JoJo 2011-04-27 18:13:47

+0

'anInstance-> publicIvar'不是使用的模式;它绕过了任何访问方法,因此意味着子类无法覆盖访问或设置的行为(这也意味着KVO将无法工作)。 “自我”是噪音;不必要。 – bbum 2011-04-27 19:27:19