iphone - 私有(private)合成属性(property)是矛盾的吗?

标签 iphone objective-c oop encapsulation

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

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. 为什么所有 私有(private)ivar 都是合成的?从整个项目来看,只有 NSMutableArray *workingArray 被外部类使用。所以其他 ivar 都不应该有 setter 和 getter。
  2. 为什么要合成非常敏感的 ivars?首先,现在 id delegate 有一个 setter,这个对象的用户可以在 XML 解析的中间切换委托(delegate),这是没有意义的。此外,NSData *dataToParse 是从网络检索的原始 XML 数据。现在它有一个 setter ,这个对象的用户可以破坏数据。
  3. 在 header 中将所有内容标记为 private 有什么意义?由于所有 ivar 都被合成为具有 getter/setter,因此它们实际上是公开的。您可以将它们设置为您想要的任何值,并且可以随时获取它们的值。

最佳答案

我在我的许多类(class)中都遵循这个例子所模仿的习语,所以我可以尝试解释我自己对这种做法的理由。

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

对于来自其他语言的开发人员来说,为私有(private)实例变量合成 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) 在这种情况下将始终为真,因此我们可以简化这个特定的分配。事实上,在大多数情况下,您可能可以通过对实例变量进行简单的直接赋值来逃脱,但当然是异常(exception)情况往往会产生最多的错误。我更喜欢安全地玩它并通过合成 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];
}

编辑:Daniel Dickison 指出了一些 在 dealloc 中使用访问器的风险 我没有考虑过。见 评论。

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

请注意,我已经设置了 self.delegate = nil;,即使该属性被声明为 (nonatomic, assign)。这个任务不是绝对必要的。事实上,我可以完全取消我的 (nonatomic, assign) 对象的属性,但我再次发现,在我所有的实例变量中始终如一地应用这个习惯用法让我的大脑更少思考,并进一步减少了我因一些粗心的错误而产生错误的可能性。如有必要,我可以简单地将属性从 (nonatomic, assign) 翻转为 (nonatomic, retain),而无需触及任何内存管理代码。我喜欢这样。

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

关于iphone - 私有(private)合成属性(property)是矛盾的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5807807/

相关文章:

objective-c - UITableView编辑模式删除按钮padding-right

javascript - JavaScript 对象中的范围问题

iphone - 自定义 UISlider,如音乐应用

ios - 减少警报 View 编码

objective-c - 在单例上实现分配

c++ - 从类中打印变量

java - 将参数传递给构造函数,替代语法

ios - 如何使用 AVPlayer 快速播放 youtube 视频?

ios - UIButton 操作在 UIView 中不起作用

ios - 代码 : How to Add Left and Right Padding on Text Field?