我在Obj-c和Cocoa有几年的经验,但我刚刚回到它和Obj-C 2.0等的进步。
我正试图绕过现代运行时并声明属性等。让我感到困惑的一件事是现代运行时能够隐式创建iVar。当然,这意味着您应该始终使用self.property来访问该值。
但是,在init *和dealloc(假设您不使用GC)方法中,我们应该直接使用iVar(在当前运行时)。
所以问题是:
我们应该在init *和dealloc中使用属性访问器与Modern Runtime吗?
如果是这样 ,为什么会有所不同?是不是因为编译器看不到iVar?
如果我需要覆盖一个访问器,我是否仍然可以访问将在运行时定义的iVar,或者我是否必须定义运行时将使用的实际iVar?
再次, 如果我可以访问合成的iVar ,为什么我不能继续为init *和dealloc方法执行此操作?
我多次阅读文档,但是他们对所有这些看起来有点模糊,我想确保我理解它以便决定如何继续编码。
希望我的问题清楚。
测试快速摘要:
如果您没有在遗产中声明ivar,编译器完全不满意
如果你在遗留编译器中使用ivar周围的#ifndef __OBJC2__
很高兴,你可以直接使用ivar和作为属性
在现代运行时,您可以将ivar保留为undefined并作为属性访问
在现代运行时,尝试直接访问ivar而不进行声明会在编译期间出错
@private
宣告ivar可以直接访问传统和现代的ivar
现在没有真正给出一个干净的方法吗?
答案 0 :(得分:10)
在当前(OS X 10.5 / GCC 4.0.1)编译器中,您无法直接访问运行时合成的ivars。 OS X运行时工程师之一Greg Parker将this方式放在cocoa-dev列表上(2009年3月12日):
你不能在当前的编译器中。一个 未来的编译器应该修复它。使用 显式的@private ivars in 与此同时。 @private ivar不应该 被视为合同的一部分 - 这就是@private的意思,强制执行 通过编译器警告和链接器 错误。
为什么没有办法 显式声明实例变量 在新运行时的.m文件中?
有三个原因:(1)有一些原因 非平凡的设计细节工作 out,(2)编译器 - 工程师 - 时间 有限的,(3)@private ivars是 一般都足够好。
因此,现在您必须使用点符号来访问属性,即使在init
和dealloc
中也是如此。这违背了在这些情况下直接使用ivars的最佳做法,但没有办法绕过它。我发现在大多数情况下,使用运行时合成的ivars(以及性能优势)的难易程度超过了这一点。如果您需要直接访问ivar,可以使用@private ivar,正如Greg Parker所建议的那样(没有任何东西阻止您混合使用显式声明和运行时合成的ivars)。
更新使用OS X 10.6,64位运行时允许通过self->ivar
直接访问合成的ivars。
答案 1 :(得分:1)
由于实例变量本身只能在现代运行时中合成(并且必须在32位或pre-Leopard下的@interface中声明),因此最安全/最可移植也要声明ivar
- 我们应该在init *和dealloc中使用属性访问器和Modern Runtime吗?
我的经验法则是-init*
“可能”,-dealloc
“通常不是”。
初始化对象时,您需要确保正确复制/保留ivars的值。除非属性的setter有一些不适合初始化的副作用,否则一定要重用属性提供的抽象。
取消分配对象时,您希望释放任何ivar对象,但不能存储新对象。一种简单的方法是将属性设置为nil(myObject.myIvar = nil
),基本上调用[myObject setMyIvar:nil]
。由于忽略了对nil的消息,因此没有危险。但是,当[myIvar发布]时,这是过度的。通常都是你需要的。通常,在释放行为与设置变量的行为不同的情况下,不要使用属性(或直接使用setter)。
我完全可以理解eJames在init / dealloc中使用属性访问器的论点,但另一方面是,如果你改变了属性行为(例如,从retain更改为copy,或者只是分配而不保留)并且不要'在init中使用它,反之亦然,行为也可能不同步。如果初始化和修改ivar的行为相同,请使用属性访问器。
- 如果是这样,为什么会有所不同?是不是因为编译器看不到ivar?
现代运行时更智能地处理类大小和布局,这就是为什么你可以改变ivars的布局而不必重新编译子类。它还能够从相应属性的名称和类型推断出您想要的ivar的名称和类型。 Objective-C 2.0 Runtime Programming Guide有更多信息,但同样,我不知道细节在那里解释得有多深。
- 如果我需要覆盖一个访问器,我是否仍然可以访问将在运行时定义的iVar,或者我是否必须定义运行时将使用的实际iVar?
我没有对此进行测试,但我相信您可以在代码中访问命名的ivar,因为它实际上必须创建。我不确定编译器是否会抱怨,但我猜想,因为它会让你在没有抱怨的情况下合成ivar,它也足够聪明地知道合成的ivar并让你按名称引用它。
- 同样,如果我可以访问合成的iVar,为什么我不能继续为init *和dealloc方法执行此操作?
您应该能够在分配实例后随时访问该属性和/或ivar。
答案 2 :(得分:0)
another SO question有类似的信息,但不是很重复。
来自Objective-C 2.0 documentation并引自Mark Bessey's answer的底线如下:
依赖于运行时的行为存在差异(另请参阅“运行时差异”):
对于遗留运行时,必须已在@interface块中声明实例变量。如果存在与属性相同名称和兼容类型的实例变量,则使用它 - 否则,您将收到编译器错误。
对于现代运行时,根据需要合成实例变量。如果已存在同名的实例变量,则使用它。
我的理解如下:
您不应在init*
和dealloc
方法中使用属性访问器,原因与您不应在旧版运行时中使用它们相同:如果您稍后覆盖了属性方法,并最终做一些不应该在init*
或dealloc
中完成的事情。
你应该能够合成ivar 和覆盖属性方法,如下所示:
@interface SomeClass
{
}
@property (assign) int someProperty;
@end
@implementation SomeClass
@synthesize someProperty; // this will synthesize the ivar
- (int)someProperty { NSLog(@"getter"); return someProperty; }
- (void)setSomeProperty:(int)newValue
{
NSLog(@"setter");
someProperty = newValue;
}
@end
这让我认为您也可以使用init*
和dealloc
方法访问合成的ivar。我能想到的唯一问题是@synthesize
行可能必须在源文件中init*
和dealloc
方法的定义之前。< / p>
最后,由于界面中声明的ivars仍然有效,这仍然是你最安全的选择。
答案 3 :(得分:0)
我遇到了同样的问题。我无法访问合成实例变量的方法如下:
公共标题
@interface MyObject:NSObject {
}
@property (retain) id instanceVar;
@property (retain) id customizedVar;
@end
私有标头/实现
@interface MyObject()
@property (retain) id storedCustomizedVar;
@end
@implementation MyObject
@synthesize instanceVar, storedCustomizedVar;
@dynamic customizedVar;
- customizedVar {
if(!self.storedCustomizedVar) {
id newCustomizedVar;
//... do something
self.storedCustomizedVar= newCustomizedVar;
}
return self.storedCustomizedVar;
}
- (void) setCustomizedVar:aVar {
self.storedCustomizedVar=aVar;
}
@end
它不是那么优雅,但至少它保持我的公共头文件干净。
如果您使用KVO,则需要将customizedVar定义为storedCustomizedVar的依赖键。
答案 4 :(得分:0)
我对Obj-C比较陌生(但不是编程),也被这个话题搞糊涂了。
让我担心的方面是,无意中使用iVar而不是属性似乎相对容易。例如写作:
myProp = someObject;
而不是
self.myProp = someObject;
不可否认这是“用户”错误,但在某些代码中看起来仍然很容易意外,对于保留或原子属性,它可能会导致问题。
理想情况下,我希望能够在生成任何 iVar时让运行时将一些模式应用于属性名称。例如。总是在前面添加“_”。
目前在实践中,我正在手动执行此操作 - 明确声明我的ivars,并故意从属性中提供不同的名称。我使用旧式'm'前缀,所以如果我的属性是“myProp”,我的iVar将是“mMyProp”。然后我使用@synthesize myProp = mMyProp将两者联系起来。
我承认这有点笨拙,还有一些额外的打字,但对我来说,能够在代码中更明确地消除歧义似乎是值得的。当然我仍然可以解决它并键入mMyProp = someObject,但我希望'm'前缀会提醒我我的错误。
如果我能够声明属性并让编译器/运行时完成剩下的工作会感觉好多了,但是当我有很多代码时,我的直觉告诉我,如果我仍然需要,我会犯这样的错误遵循init / dealloc的手动规则。
当然还有很多其他我也做错的事情......