私人合成财产是矛盾吗?

JoJ*_*oJo 8 oop iphone encapsulation objective-c

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

在Apple提供的延迟表图像加载上查看此示例模板:

@interface ParseOperation : NSOperation <NSXMLParserDelegate>

{
@private
    id <ParseOperationDelegate> delegate;
    NSData          *dataToParse;
    NSMutableArray  *workingArray;
    AppRecord       *workingEntry;
    NSMutableString *workingPropertyString;
    NSArray         *elementsToParse;
    BOOL            storingCharacterData;
}
Run Code Online (Sandbox Code Playgroud)

资源

@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;
Run Code Online (Sandbox Code Playgroud)

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

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

cdu*_*uhn 10

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

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

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

考虑一下:

self.workingPropertyString = [NSMutableString string];
Run Code Online (Sandbox Code Playgroud)

与此相对:

workingPropertyString = [[NSMutableString string] retain];
Run Code Online (Sandbox Code Playgroud)

许多开发人员声称这两个任务在功能上是等同的,但是有一个重要的区别.如果workingPropertyString已经指向保留的对象,则第二个赋值会泄漏内存.要编写与合成setter功能相同的代码,您必须执行以下操作:

NSMutableString *newString = [NSMutableString string];
if (workingPropertyString != newString) {
    [workingPropertyString release];
    workingPropertyString = [newString retain];
}
Run Code Online (Sandbox Code Playgroud)

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

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

self.foo = [Foo fooWithTitle:@"The Foo"];
Run Code Online (Sandbox Code Playgroud)

或这个:

self.foo = [[[Foo alloc] initWithTitle:@"The Foo"] autorelease];
Run Code Online (Sandbox Code Playgroud)

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

我喜欢这种做法的另一件事是我的dealloc方法看起来像这样:

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

编辑:丹尼尔迪克森指出在dealloc中使用访问器的一些风险,我没有考虑过.查看评论.

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

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

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

  • 很好的答案,但一个挑剔:设置属性为nil而不是直接在`dealloc`方法中调用`release`是不鼓励的(有一些Apple文档说,虽然我现在找不到它).原因是setter可以触发KVO通知,这可能导致观察者试图访问部分解除分配的实例. (5认同)