私人综合财产是矛盾的吗?
在阅读了初学者的 iPhone 开发人员书籍并在线阅读示例代码后,我注意到大多数 Objective C 程序员都会综合几乎所有实例变量。有些变量很容易合成,但大多数变量在遵循面向对象的封装原则时不应该这样做。最糟糕的是标记为私有的综合财产。尝试使用其他人的代码的 C++ 程序员将读取头文件中的公共字段和方法。他们将跳过私有变量。该 C++ 程序员不会知道您打算以某种有意义的方式使用私有属性。
标题
@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 应该有远离一般编程实践的充分理由。
- 为什么所有私有ivars都被合成?当您将项目视为一个整体时,只有
NSMutableArray *workingArray
被外部类使用。所以其他 ivars 都不应该有 setter 和 getter。 - 为什么要合成非常敏感的ivars?首先,现在 id delegate 有了 setter,该对象的用户可以在 XML 解析过程中切换委托,这是没有意义的。另外,
NSData *dataToParse
是从网络检索的原始 XML 数据。现在它有了一个设置器,该对象的用户可以损坏数据。 - 将标头中的所有内容标记为
private
有何意义?由于所有 ivars 都被合成为具有 getter/setter,因此它们实际上是公共的。您可以将它们设置为您想要的任何值,并且可以随时获取它们的值。
After going through a beginner's iPhone developer book and reading sample code online, I've noticed that most Objective C programmers synthesize nearly every instance variable. Some variables are convenient to snythesize, but most should not when honoring the object oriented principle of encapsulation. The worst are synthetized properties marked as private. A C++ programmer trying to use someone else's code will read the public fields and methods in the header file. They will skip the private variables. This C++ programmer will not know that you intended the private properties to be used in some meaningful way.
Take a look at this sample template on lazy table image loading provided by Apple:
Header
@interface ParseOperation : NSOperation <NSXMLParserDelegate>
{
@private
id <ParseOperationDelegate> delegate;
NSData *dataToParse;
NSMutableArray *workingArray;
AppRecord *workingEntry;
NSMutableString *workingPropertyString;
NSArray *elementsToParse;
BOOL storingCharacterData;
}
Source
@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;
Now I know this is not C++ and we shouldn't assume all C++ practices should be honored in Objective C. But Objective C should have good reasons to stray away from general programming practices.
- Why are all the private ivars synthesized? When you look at the project as a whole, only
NSMutableArray *workingArray
is used by outside classes. So none of the other ivars should have setters and getters. - Why are very sensitive ivars synthesized? For one, now that
id delegate
has a setter, the user of this object can switch the delegate in middle of the XML parsing, something that doesn't make sense. Also,NSData *dataToParse
is raw XML data retrieved from the network. Now that it has a setter, the user of this object can corrupt the data. - What's the point of marking everything
private
in the header? Since all ivars are are synthesized to have getters/setters, they are effectively public. You can set them to anything you want and you can get their value whenever you want.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我在许多课程中都遵循这个例子所模仿的习语,所以我可以尝试解释我自己对这种做法的理由。
此示例中的属性在 .m 文件的类扩展中声明。这使得它们实际上是私有的。任何从另一个类访问这些属性的尝试都会在编译时导致“未找到属性”错误。
对于来自其他语言的开发人员来说,为私有实例变量综合 getter 和 setter 可能看起来很奇怪。事实上,我这样做的原因只有一个。如果一致使用,综合属性可以简化内存管理,并有助于避免可能导致错误的粗心错误。这里有几个例子:
考虑这个:
与这个:
许多开发人员会声称这两个分配在功能上是等效的,但有一个重要的区别。如果workingPropertyString 已经指向保留的对象,则第二个分配会泄漏内存。要编写与合成 setter 功能等效的代码,您必须执行以下操作:
此代码避免泄漏实例变量可能指向的任何现有对象,并且它安全地处理您可能重新分配实例变量的可能性。与实例变量相同的对象。综合设置器会为您完成所有这些工作。
当然,我们可以看到在这种情况下
(workingPropertyString != newString)
始终为 true,因此我们可以简化这个特定的分配。事实上,在大多数情况下,您可能可以通过对实例变量进行简单的直接赋值来逃脱,但当然,例外情况往往会产生最多的错误。我更喜欢谨慎行事,并通过合成设置器设置所有对象实例变量。我所有的实例对象分配都是简单的行话,看起来像这样:或者这样:
这种简单性和一致性让我弱小的大脑不需要思考什么。因此,我几乎从来没有遇到过与内存管理相关的错误。 (我知道 autorelease 理论上可能会在紧密循环中消耗过多的内存,但我在实践中还没有遇到过这个问题。如果我遇到过,这也是一个简单的优化案例。)
我喜欢这种做法的另一件事是我的 dealloc 方法都看起来像这样:
编辑:Daniel Dickison 指出了一些
在 dealloc 中使用访问器的风险
我没有考虑过。请参阅
注释。
其中每个对象属性都简单地设置为 nil。这会同时释放每个保留的属性,同时将其设置为 nil,以避免由于 EXC_BAD_ACCESS 导致的某些崩溃。
请注意,我设置了
self.delegate = nil;
,即使该属性被声明为(nonatomic, allocate)
。这项任务并不是绝对必要的。事实上,我可以完全取消我的(nonatomic, allocate)
对象的属性,但我再次发现,在所有实例变量中一致地应用这个习惯用法可以让我的大脑减少思考,并进一步减少了我因一些粗心错误而产生错误的机会。如果有必要,我可以简单地将属性从(非原子,分配)
翻转为(非原子,保留)
,而无需触及任何内存管理代码。我喜欢这样。人们还可以使用一致性作为综合私有标量变量属性的参数,正如您的示例在
BOOL storageCharacterData;
的情况下所做的那样。这种做法确保每个实例变量赋值都类似于self.foo = bar;
。我通常不会自己创建私有标量属性,但我可以看到这种做法的一些理由。I follow the idiom modeled by this example in many of my classes, so I can try to explain my own justification for this practice.
The properties in this example are declared in a class extension in the .m file. This makes them effectively private. Any attempt to access these properties from another class will cause a "Property not found" error upon compilation.
For developers coming from other languages, it may seem strange to synthesize getters and setters for private instance variables. Indeed, there is only one reason why I do this. When used consistently, synthesized properties can simplify memory management and help avoid careless mistakes that can lead to bugs. Here are a couple of examples:
Consider this:
versus this:
Many developers would claim that these two assignments are functionally equivalent, but there's an important difference. The second assignment leaks memory if workingPropertyString was already pointing at a retained object. To write code functionally equivalent to the synthesized setter, you'd have to do something like this:
This code avoids leaking any existing object that the instance variable may be pointing to, and it safely handles the possibility that you may be re-assigning the same object to the instance variable. The synthesized setter does all of this for you.
Of course we can see that
(workingPropertyString != newString)
will always be true in this case, so we could simplify this particular assignment. In fact in most cases you can probably get away with a simple direct assignment to an instance variable, but of course it's the exceptional cases that tend to create the most bugs. I prefer to play it safe and set all my object instance variables through synthesized setters. All my instance object assignments are simple one-liners that look like this:or this:
This simplicity and consistency gives my feeble brain less stuff to think about. As a result I almost never have bugs related to memory management. (I'm aware that the
autorelease
idiom could theoretically consume excessive memory in a tight loop, but I have yet to encounter that issue in practice. If I ever do, it's a simple case to optimize.)One other thing I like about this practice is that my dealloc methods all look like this:
EDIT: Daniel Dickison pointed out some
risks to using accessors in dealloc
that I hadn't considered. See the
comments.
where every object property is simply set to nil. This simultaneously releases each retained property while setting it to nil to avoid certain crashes due to EXC_BAD_ACCESS.
Note that I've set
self.delegate = nil;
even though that property was declared as(nonatomic, assign)
. This assignment wasn't strictly necessary. In fact, I could do away with properties for my(nonatomic, assign)
objects altogether, but again I've found that applying this idiom consistently across all my instance variables gives my brain less to think about, and further reduces the chance that I'll create a bug through some careless mistake. If necessary I can simply flip a property from(nonatomic, assign)
to(nonatomic, retain)
without having to touch any memory management code. I like that.One could also use consistency as an argument for synthesizing properties for private scalar variables, as your example has done in the case of
BOOL storingCharacterData;
. This practice ensures that every instance variable assignment will look likeself.foo = bar;
. I don't usually bother to create private scalar properties myself, but I can see some justification for this practice.没有真正的需要;如果您要直接访问所有 ivars,则不需要 @synthesize。
没有任何 setter/getter 被公开声明。如果类的客户端想要通过在中间切换委托来破坏事物,他们必须破坏封装才能做到这一点。
所以,最终,这不是问题。
请注意,在该示例中甚至不需要声明 ivars;编译器会根据@property声明自动合成它们。
传统上,@private 可以防止有人直接从外部将 ivar 欺骗到类的实例。
请注意,anInstance->ivar 或 self->ivar 几乎从未使用过(并且,使用时,几乎总是出于错误的原因)。它有用途,但很少见。
No real need; if you are going to access all the ivars directly anyway, there is no need for @synthesize.
None of the setter/getters are publicly declared. If a client of the class wanted to corrupt things by switching the delegate in the middle, they'd have to break encapsulation to do so.
So, ultimately, a non-issue.
Note that there is no need to even declare the ivars in that example; the compiler will automatically synthesize them based on the @property declaration.
Traditionally, @private protected against someone diddling the ivar directly from externally to an instance of the class.
Note that anInstance->ivar or self->ivar is almost never used (and, when used, it is almost always for the wrong reason). There are uses for it, but it is rare.