在 Modern Runtime 中使用实例变量
我在 Obj-c 和 Cocoa 方面有几年的经验,但现在才回到它以及 Obj-C 2.0 的进步等。
我正在尝试了解现代运行时和声明属性等。一让我有点困惑的是现代运行时能够隐式创建 iVar。 当然,这意味着在您的代码中您应该始终使用 self.property 来访问该值。
但是,在 init* 和 dealloc(假设您没有使用 GC)方法中,我们应该直接使用 iVar(在当前运行时)。
所以问题是:
我们应该在 init* 中使用属性访问器并在 Modern Runtime 中使用 dealloc 吗?
如果是这样,为什么会有所不同? 难道只是因为编译器看不到 iVar?
如果我需要重写访问器,我仍然可以访问将在运行时定义的 iVar,还是必须定义运行时将使用的实际 iVar?
- 一次,如果我可以访问合成的 iVar,为什么我不能继续对 init* 和 dealloc 方法执行此操作?
我读了几次文档,但它们似乎对所有这些都有点模糊,我想确保我很好地理解它,以便决定我要如何继续编码。
希望我的问题很清楚。
测试快速总结:
如果你没有在legacy中声明ivar,编译器会完全不高兴
- < p>如果您在遗留编译器中的 ivar 周围使用
#ifndef __OBJC2__
很高兴,您可以直接使用 ivar 并作为属性 在现代运行时,您可以将 ivar 保留为未定义并作为属性访问
在现代运行时,尝试直接访问 ivar 而不声明会在编译期间出错
@private
当然,声明 ivar 允许直接访问 ivar,无论是旧版还是现代版
版现在并没有真正给出一个干净的前进方式,不是吗?
I have several years of experience in Obj-c and Cocoa, but am just now getting back into it and the advances of Obj-C 2.0 etc.
I'm trying to get my head around the modern runtime and declaring properties, etc. One thing that confuses me a bit is the ability in the modern runtime to have the iVars created implicitly. And of course this implies that in your code you should always be using self.property to access the value.
However, in init* and dealloc(assuming you're not using GC) methods we should be using the iVar directly (in the current runtime).
So questions are:
Should we use property accessors in init* and dealloc with Modern Runtime?
If so, why is this different? Is it just because the compiler can't see the iVar?
If I need to override an accessor, can I still access that iVar that will be defined at runtime or do I have to define an actual iVar that the runtime will then use?
Again, if I can access the synthesized iVar, why can't I continue to do this for the init* and dealloc methods?
I read the docs several times, but they seemed a bit vague about all of this and I want to be sure that I understand it well in order to decide how I want to continue coding.
Hope that my questions are clear.
Quick summary of testing:
If you don't declare the ivar in legacy, compiler is completely unhappy
If you use
#ifndef __OBJC2__
around ivar in legacy compiler is happy and you can use both ivar directly and as propertyIn modern runtime, you can leave the ivar undefined and access as property
In modern runtime, trying to access ivar directly without declaration gives error during compile
@private
declaration of ivar, of course, allows direct access to ivar, in both legacy and modern
Doesn't really give a clean way to go forward right now does it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
在当前(OS X 10.5/GCC 4.0.1)编译器中,您无法直接访问运行时合成的ivars。 OS X 运行时工程师之一 Greg Parker 指出 在 cocoa-dev 列表中这样(2009 年 3 月 12 日):
因此,现在您必须使用点表示法来访问属性,即使在
init
和dealloc
中也是如此。 这违背了在这些情况下直接使用 ivars 的最佳实践,但没有办法解决这个问题。 我发现在大多数情况下,使用运行时合成的 ivars 的便利性(以及性能优势)超过了这一点。 如果您确实需要直接访问 ivar,则可以按照 Greg Parker 的建议使用 @private ivar(没有什么可以阻止您混合显式声明的 ivar 和运行时合成的 ivar)。更新 在 OS X 10.6 中,64 位运行时确实允许通过
self->ivar
直接访问合成的 ivars。In the current (OS X 10.5/GCC 4.0.1) compiler, you cannot directly access the runtime-synthesized ivars. Greg Parker, one of the OS X runtime engineers put it this way on the cocoa-dev list (March 12, 2009):
So, for now you must use dot-notation to access properties, even in
init
anddealloc
. This goes against the best practice of using ivars directly in these cases, but there's no way around it. I find that the ease of using runtime-synthesized ivars (and the performance benefits) outweigh this in most cases. Where you do need to access the ivar directly, you can use a @private ivar as Greg Parker suggests (there's nothing that prevents you from mixing explicitly declared and runtime-synthesized ivars).Update With OS X 10.6, the 64-bit runtime does allow direct access to the synthesized ivars via
self->ivar
.由于实例变量本身只能在现代运行时合成(并且必须在 32 位或 Leopard 之前的 @interface 中声明),因此声明 ivar 是最安全/最可移植的
我的经验法则是,
-init*
是“可能”,-dealloc
是“通常不会”。初始化对象时,您需要确保正确复制/保留 ivars 的值。 除非属性的设置器有一些副作用使其不适合初始化,否则一定要重用属性提供的抽象。
释放对象时,您希望释放所有 ivar 对象,但不存储新对象。 执行此操作的一个简单方法是将属性设置为 nil (
myObject.myIvar = nil
),这基本上会调用[myObject setMyIvar:nil]
。 由于传递给 nil 的消息会被忽略,因此不会有任何危险。 然而,当 [myIvar release] 时,这就有点矫枉过正了; 通常就是您所需要的。 一般来说,在释放行为与设置变量的行为不同的情况下,不要使用该属性(或直接使用 setter)。我完全可以理解 eJames 反对在 init/dealloc 中使用属性访问器的论点,但另一方面是,如果您更改属性行为(例如,从保留更改为复制,或者只是分配而不保留)并且不使用它在 init 中,反之亦然,行为也可能不同步。 如果初始化和修改 ivar 的行为应该相同,请对两者使用属性访问器。
现代运行时更智能地处理类大小和布局,这就是为什么您可以更改 ivars 的布局而无需重新编译子类。 它还能够从相应属性的名称和类型推断出您想要的 ivar 的名称和类型。 Objective-C 2.0 运行时编程指南提供了更多信息,但同样,我不知道那里的细节解释得有多深入。
我还没有对此进行测试,但我相信您可以在代码中访问命名的 ivar,因为它实际上必须创建。 我不确定编译器是否会抱怨,但我猜想,既然它会让你合成 ivar 而不会抱怨,它也足够聪明,可以了解合成的 ivar 并让你通过名称引用它。
分配实例后,您应该能够随时访问该属性和/或 ivar。
Since instance variables themselves can only be synthesized in the modern runtime (and must be declared in the @interface under 32-bit or pre-Leopard), it's safest / most portable to also declare the ivar
My rule of thumb is "possibly" for
-init*
, and "usually not" for-dealloc
.When initializing an object, you want to make sure to properly copy/retain values for ivars. Unless the property's setter has some side effect that makes it inappropriate for initialization, definitely reuse the abstraction the property provides.
When deallocating an object, you want to release any ivar objects, but not store new ones. An easy way to do this is to set the property to nil (
myObject.myIvar = nil
), which basically calls[myObject setMyIvar:nil]
. Since messages to nil are ignored, there is no danger in this. However, it's overkill when [myIvar release]; is usually all you need. In general, don't use the property (or directly, the setter) in situations where deallocation should behave differently than setting the variable.I can understand eJames' argument against using property accessors in init/dealloc at all, but the flipside is that if you change the property behavior (for example, change from retain to copy, or just assign without retaining) and don't use it in init, or vice versa, the behavior can get out of sync too. If initializing and modifying an ivar should act the same, use the property accessor for both.
The modern runtime deals with class size and layout more intelligently, which is why you can change the layout of ivars without having to recompile subclasses. It is also able to infer the name and type of the ivar you want from the name and type of the corresponding property. The Objective-C 2.0 Runtime Programming Guide has more info, but again, I don't know how deeply the details explained there.
I haven't tested this, but I believe you're allowed to access the named ivar in code, since it actually does have to be created. I'm not sure whether the compiler will complain, but I would guess that since it will let you synthesize the ivar without complaining, it is also smart enough to know about the synthesized ivar and let you refer to it by name.
You should be able to access the property and/or ivar anytime after the instance has been allocated.
还有另一个SO问题具有类似的信息,但它并不完全重复。
底线,来自 Objective-C 2.0 文档,并引用自 马克·贝西的回答如下:
我的理解如下:
您不应在 init* 和 dealloc 方法中使用属性访问器,其原因与您不应在遗留运行时中使用它们的原因相同:如果您稍后重写属性方法,并最终执行不应该在
init*
或dealloc
中执行的操作,则会面临潜在的错误。您应该能够合成 ivar 并覆盖属性方法,如下所示:
这让我认为您将能够在
init*
中访问合成的 ivar以及dealloc方法。 我能想到的唯一问题是@synthesize
行可能必须出现在init*
和dealloc 的定义之前
源文件中的方法。最后,由于在接口中声明的 ivars 仍然有效,所以这仍然是您最安全的选择。
There is another SO question with similar information, but it isn't quite a duplicate.
The bottom line, from the Objective-C 2.0 documentation, and quoted from Mark Bessey's answer is as follows:
My understanding is as follows:
You should not use property accessors in
init*
anddealloc
methods, for the same reasons that you should not use them in the legacy runtime: It leaves you open to potential errors if you later override the property methods, and end up doing something that shouldn't be done ininit*
ordealloc
.You should be able to both synthesize the ivar and override the property methods as follows:
Which leads me to think that you would be able to access the synthesized ivar in your
init*
anddealloc
methods as well. The only gotcha I could think of is that the@synthesize
line may have to come before the definitions of yourinit*
anddealloc
methods in the source file.In the end, since having the ivars declared in the interface still works, that is still your safest bet.
我遇到了同样的问题。 我解决无法访问合成实例变量的方法如下:
public header
private header /implementation
它不是那么优雅,但至少它使我的公共头文件保持干净。
如果您使用 KVO,则需要将customizedVar 定义为storedCustomizedVar 的依赖键。
I am running into the same problem. The way I am working around not being able to access the synthesized instance variables is the following:
public header
private header / implementation
It's not that elegant, but at least it keeps my public header file clean.
If you use KVO you need to define customizedVar as dependent key of storedCustomizedVar.
我对 Obj-C 比较陌生(但不是编程),并且也对这个主题感到困惑。
让我担心的方面是,似乎相对容易无意中使用 iVar 而不是属性。 例如写:
myProp = someObject;
而不是
self.myProp = someObject;
诚然,这是“用户”错误,但在某些代码中似乎仍然很容易意外执行,并且对于保留或原子属性,它可能会导致问题。
理想情况下,我希望能够让运行时在生成任何 iVar 时将某种模式应用于属性名称。 例如,始终使用“_”作为前缀。
目前在实践中,我正在手动执行此操作 - 显式声明我的 ivars,并故意为它们提供与属性不同的名称。 我使用旧式“m”前缀,因此如果我的属性是“myProp”,我的 iVar 将是“mMyProp”。 然后我使用 @synthesize myProp = mMyProp 将两者关联起来。
我承认这有点笨拙,而且需要额外输入一些内容,但对我来说,能够在代码中更清楚地消除歧义似乎是值得的。 当然,我仍然可能会出错并输入 mMyProp = someObject,但我希望“m”前缀能够提醒我注意我的错误。
如果我可以只声明属性并让编译器/运行时完成其余的事情,那感觉会好得多,但是当我有很多代码时,我的直觉告诉我,如果我仍然必须遵循手动规则,我会犯这样的错误用于初始化/释放。
当然,我也可能做错很多其他事情......
I'm relatively new to Obj-C (but not to programming) and have also been confused by this topic.
The aspect that worries me is that it seems to be relatively easy to inadvertently use the iVar instead of the property. For example writing:
myProp = someObject;
instead of
self.myProp = someObject;
Admittedly this is "user" error, but it's still seems quite easy to do accidentally in some code, and for a retained or atomic property it could presumably lead to problems.
Ideally I'd prefer to be able to get the runtime to apply some pattern to the property name when generating any iVar. E.g. always prefix them with "_".
In practice at the moment I'm doing this manually - explicitly declaring my ivars, and deliberately giving them different names from the properties. I use an old-style 'm' prefix, so if my property is "myProp", my iVar will be "mMyProp". Then I use @synthesize myProp = mMyProp to associate the two.
This is a bit clumsy I admit, and a bit of extra typing, but it seems worth it to me to be able to disambiguate a little bit more clearly in the code. Of course I can still get it wrong and type mMyProp = someObject, but I'm hoping that the 'm' prefix will alert me to my error.
It would feel much nicer if I could just declare the property and let the compiler/runtime do the rest, but when I have lots of code my gut instinct tells me that I'll make mistakes that way if I still have to follow manual rules for init/dealloc.
Of course there are also plenty of other things I can also do wrong...