说我有这样的课程:
@interface MyAwesomeClass : NSObject
{
@private
NSString *thing1;
NSString *thing2;
}
@property (retain) NSString *thing1;
@property (retain) NSString *thing2;
@end
@implementation MyAwesomeClass
@synthesize thing1, thing1;
@end
在内部访问thing1
和thing2
时(即在MyAwesomeClass
的实施中),使用该属性是否更好,或者只是引用直接实例变量(假设我们在“自定义”访问或更改器中不做任何工作的情况,即我们只是设置并获取变量)。在Pre-Objective C 2.0之前,我们通常只是直接访问ivars,但现在通常的编码风格/最佳实践是什么?如果实例变量/属性是私有的并且在类之外根本无法访问,那么此建议是否会更改?您是否应该为每个ivar创建一个属性,即使它们是私有的,或仅用于面向公众的数据?如果我的应用程序不使用键值编码功能(因为KVC仅触发属性访问),该怎么办?
我有兴趣超越低级别的技术细节。例如,给定(次优)代码如:
@interface MyAwesomeClass : NSObject
{
id myObj;
}
@proprety id myObj;
@end
@implementation MyAwesomeClass
@synthesize myObj;
@end
我知道myObj = anotherObject
在功能上与self.myObj = anotherObj
相同。
但是,属性不仅仅是用于指示编译器为您编写访问器和更改器的精确语法;它们也是一种更好地封装数据的方法,即,您可以更改类的内部实现,而无需重写依赖于这些属性的类。在处理类本身的内部代码时,我对解决这个封装问题的重要性的答案很感兴趣。此外,正确编写的属性可以触发KVC通知,但不会直接进行ivar访问;如果我的应用程序没有使用KVC功能现在,以防万一将来可能,这是否重要?
答案 0 :(得分:11)
如果你花时间在cocoa-dev邮件列表上,你会发现这是一个非常有争议的话题。
有些人认为ivars应该只在内部使用,并且除了外部之外,不应该(或很少)使用属性。 KVO通知和访问者副作用存在各种问题。
有些人认为你应该总是(或大部分)使用属性而不是ivars。这里的主要优点是你的内存管理很好地包含在访问器方法中,而不是散布在你的实现逻辑中。可以通过创建指向相同ivar的单独属性来克服KVO通知和访问者副作用。
查看Apple的示例代码将会发现他们在这个主题上已经到处都是。一些样本在内部使用属性,一些使用ivars。
总的来说,我会说,这是一个品味问题,没有正确的方法去做。我自己使用两种风格的混合。
答案 1 :(得分:8)
我认为没有办法'更好'。你会看到两种风格都很常用,所以现在甚至没有通常/最好的做法。根据我的经验,使用的样式对我消化我正在寻找的一些实现文件的效果影响很小。在查看其他人的代码时,您当然希望对这两种风格(以及其中任何风格)感到满意。
在维护方面,每个内部ivar使用一个属性可能会略微过火。我已经完成了它,并且它增加了一些非常重要的工作,我认为这些工作并没有给我带来回报。但是如果你有强烈的愿望/ OCD来看到像self.var
这样的一致代码,并且你每次看到一个类时都会在脑海中拥有它,那么就使用它。不要忽视唠叨感对生产力的影响。
异常 - 显然,对于自定义getter(例如延迟创建),你没有太多选择。此外,我还为内部setter创建并使用了一个属性,使其更方便(例如,设置具有所有权语义的对象)。
“以防万一”,“可能”不是一个令人信服的理由,在没有更多数据的情况下做某事,因为实现它所需的时间不是零。一个更好的问题可能是,某些类中的所有私人ivars将来需要KVC通知的概率是多少,但现在不是?对于我自己的大多数课程,答案非常低,所以我现在避免为每个私人ivar创建属性的硬性规则。
我发现在处理内部实现时,我很快就可以很好地处理每个ivar应该如何被访问。
如果您有兴趣,我自己的方法就是:
alloc/dealloc
。在其他地方,如果存在私人财产。 答案 2 :(得分:3)
thing1 = something;
和self.thing1 = something;
分配的唯一区别在于,如果您想要进行属性分配操作(retain
,copy
等),请完成分配的对象,然后您需要使用属性。没有属性的分配实际上就是分配对提供的对象的引用。
我认为不需要为内部数据定义属性。只定义经常访问的ivars的属性,并且需要特定的mutator行为。
答案 3 :(得分:2)
如果将thing1
与KVO一起使用,最好在设置时使用self.thing1=
。如果thing1
是@public
,那么最好假设某人有时会想要将其与KVO一起使用。
如果thing1
具有复杂的设置语义,您不想在任何地方重复设置它(例如retain
或非 - nonatomic
),请使用self.thing1=
是个好主意。
如果基准测试表明调用setThing1:
占用了大量时间,那么您可能想要考虑如何设置它而不使用self.thing1=
- 也许请注意它不能是KVO,或者看看手动实现KVO是否更好(例如,如果你在一个循环中设置它3000次,你可能能够通过self->thing1
设置3000次,然后进行2次KVO调用,关于值即将改变和改变了。)
在私有变量上留下一个简单的setter的情况,你知道你没有使用KVO。此时它不再是技术问题,而且属于代码风格。至少只要访问者不会在分析器中显示为瓶颈。我倾向于在那时使用直接的ivar访问(除非我认为我将来会KVO这个价值,或者可能想让它公开,因此认为其他人可能想要KVO)。
然而,当我使用直接ivar访问设置时,我尝试只通过self->thing1=
进行操作,这使得查找所有内容变得更加简单,如果我发现需要使用KVO,则更改它们,或者公开,或制作更复杂的访问者。
答案 4 :(得分:0)
这里提到的其他事情都可以。其他答案遗漏的一些事情是:
首先,请始终牢记访问器/ mutator是虚拟的含义(就像所有Objective-C方法一样。)一般来说,人们应该避免在init和dealloc中调用虚方法,因为你没有知道子类会做什么可能会搞砸你。出于这个原因,我通常尝试直接在init和dealloc中访问iVars,并通过其他地方的访问器/ mutator访问它们。另一方面,如果您不在所有其他位置始终使用访问器,则可能会影响覆盖它们的子类。
相关地,如果您在init和amp;之外的任何地方直接访问iVar,则无法为任何人维护属性的原子性保证(即,您的@properties被声明为原子)。 dealloc的。如果你需要某些东西是原子的,不要直接访问iVar而丢掉原子性。同样,如果您不需要这些保证,请将您的属性声明为非原子(为了提高性能)。
这也与KVO问题有关。在init中,没有人可能正在观察你(合法地),并且在dealloc中,任何剩余的观察者都有一个陈旧的未经证实(即虚假)的引用。同样的推理也适用于属性的原子性保证。 (即如何在init返回之前发生并发访问,并且在dealloc期间发生的访问本身就是错误。)
如果混合使用直接和访问器/ mutator,你不仅可能与KVO和原子性发生冲突,还可能与子类的冲突。