在 Modern Runtime 中使用实例变量

发布于 2024-07-24 01:23:14 字数 1048 浏览 3 评论 0原文

我在 Obj-c 和 Cocoa 方面有几年的经验,但现在才回到它以及 Obj-C 2.0 的进步等。

我正在尝试了解现代运行时和声明属性等。一让我有点困惑的是现代运行时能够隐式创建 iVar。 当然,这意味着在您的代码中您应该始终使用 self.property 来访问该值。

但是,在 init* 和 dealloc(假设您没有使用 GC)方法中,我们应该直接使用 iVar(在当前运行时)。

所以问题是:

  1. 我们应该在 init* 中使用属性访问器并在 Modern Runtime 中使用 dealloc 吗?

  2. 如果是这样,为什么会有所不同? 难道只是因为编译器看不到 iVar?

  3. 如果我需要重写访问器,我仍然可以访问将在运行时定义的 iVar,还是必须定义运行时将使用的实际 iVar?

  4. 一次,如果我可以访问合成的 iVar,为什么我不能继续对 init* 和 dealloc 方法执行此操作?

我读了几次文档,但它们似乎对所有这些都有点模糊,我想确保我很好地理解它,以便决定我要如何继续编码。

希望我的问题很清楚。


测试快速总结

  1. 如果你没有在legacy中声明ivar,编译器会完全不高兴

  2. < p>如果您在遗留编译器中的 ivar 周围使用 #ifndef __OBJC2__ 很高兴,您可以直接使用 ivar 并作为属性

  3. 在现代运行时,您可以将 ivar 保留为未定义并作为属性访问

  4. 在现代运行时,尝试直接访问 ivar 而不声明会在编译期间出错

  5. @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:

  1. Should we use property accessors in init* and dealloc with Modern Runtime?

  2. If so, why is this different? Is it just because the compiler can't see the iVar?

  3. 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?

  4. 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:

  1. If you don't declare the ivar in legacy, compiler is completely unhappy

  2. If you use #ifndef __OBJC2__ around ivar in legacy compiler is happy and you can use both ivar directly and as property

  3. In modern runtime, you can leave the ivar undefined and access as property

  4. In modern runtime, trying to access ivar directly without declaration gives error during compile

  5. @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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

知你几分 2024-07-31 01:23:14

在当前(OS X 10.5/GCC 4.0.1)编译器中,您无法直接访问运行时合成的ivars。 OS X 运行时工程师之一 Greg Parker 指出 在 cocoa-dev 列表中这样(2009 年 3 月 12 日):

在当前的编译器中不能。 A
未来的编译器应该修复这个问题。 使用
显式@private ivars在
与此同时。 @private ivar 不应该
被视为合同的一部分 -
这就是@private 的意思,强制执行
通过编译器警告和链接器
错误。

为什么没有办法
显式声明实例变量
在新运行时的 .m 文件中?

三个原因:(1)有一些
重要的设计细节
出,(2) 编译器工程师时间是
有限,并且 (3) @private ivars 是
总体来说已经足够好了。

因此,现在您必须使用点表示法来访问属性,即使在 initdealloc 中也是如此。 这违背了在这些情况下直接使用 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):

You can't in the current compiler. A
future compiler should fix that. Use
explicit @private ivars in the
meantime. An @private ivar should not
be considered part of the contract -
that's what @private means, enforced
by compiler warnings and linker
errors.

And why isn't there a way to
explicitly declare instance variables
in the .m file for the new runtime?

Three reasons: (1) there are some
non-trivial design details to work
out, (2) compiler-engineer-hours are
limited, and (3) @private ivars are
generally good enough.

So, for now you must use dot-notation to access properties, even in init and dealloc. 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.

翻身的咸鱼 2024-07-31 01:23:14

由于实例变量本身只能在现代运行时合成(并且必须在 32 位或 Leopard 之前的 @interface 中声明),因此声明 ivar 是最安全/最可移植的

  • 我们应该在 init* 中使用属性访问器并通过 Modern Runtime 进行 dealloc 吗?

我的经验法则是,-init* 是“可能”,-dealloc 是“通常不会”。

初始化对象时,您需要确保正确复制/保留 ivars 的值。 除非属性的设置器有一些副作用使其不适合初始化,否则一定要重用属性提供的抽象。

释放对象时,您希望释放所有 ivar 对象,但不存储新对象。 执行此操作的一个简单方法是将属性设置为 nil (myObject.myIvar = nil),这基本上会调用 [myObject setMyIvar:nil]。 由于传递给 nil 的消息会被忽略,因此不会有任何危险。 然而,当 [myIvar release] 时,这就有点矫枉过正了; 通常就是您所需要的。 一般来说,在释放行为与设置变量的行为不同的情况下,不要使用该属性(或直接使用 setter)。

我完全可以理解 eJames 反对在 init/dealloc 中使用属性访问器的论点,但另一方面是,如果您更改属性行为(例如,从保留更改为复制,或者只是分配而不保留)并且不使用它在 init 中,反之亦然,行为也可能不同步。 如果初始化和修改 ivar 的行为应该相同,请对两者使用属性访问器。

  • 如果是这样,为什么会有所不同? 难道只是因为编译器看不到ivar?

现代运行时更智能地处理类大小和布局,这就是为什么您可以更改 ivars 的布局而无需重新编译子类。 它还能够从相应属性的名称和类型推断出您想要的 ivar 的名称和类型。 Objective-C 2.0 运行时编程指南提供了更多信息,但同样,我不知道那里的细节解释得有多深入。

  • 如果我需要重写访问器,我仍然可以访问将在运行时定义的 iVar,还是必须定义运行时将使用的实际 iVar?

我还没有对此进行测试,但我相信您可以在代码中访问命名的 ivar,因为它实际上必须创建。 我不确定编译器是否会抱怨,但我猜想,既然它会让你合成 ivar 而不会抱怨,它也足够聪明,可以了解合成的 ivar 并让你通过名称引用它。

  • 再说一次,如果我可以访问合成的 iVar,为什么我不能继续对 init* 和 dealloc 方法执行此操作?

分配实例后,您应该能够随时访问该属性和/或 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

  • Should we use property accessors in init* and dealloc with Modern Runtime?

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.

  • If so, why is this different? Is it just because the compiler can't see the ivar?

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.

  • 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?

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.

  • Again, if I can access the synthesized iVar, why can't I continue to do this for the init* and dealloc methods?

You should be able to access the property and/or ivar anytime after the instance has been allocated.

一紙繁鸢 2024-07-31 01:23:14

还有另一个SO问题具有类似的信息,但它并不完全重复。

底线,来自 Objective-C 2.0 文档,并引用自 马克·贝西的回答如下:

依赖于运行时的行为存在差异(另请参阅“运行时差异”):

对于旧版运行时,必须已在 @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* 中访问合成的 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:

There are differences in the behavior that depend on the runtime (see also “Runtime Differences”):

For the legacy runtimes, instance variables must already be declared in the @interface block. If an instance variable of the same name and compatible type as the property exists, it is used—otherwise, you get a compiler error.

For the modern runtimes, instance variables are synthesized as needed. If an instance variable of the same name already exists, it is used.

My understanding is as follows:

You should not use property accessors in init* and dealloc 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 in init* or dealloc.

You should be able to both synthesize the ivar and override the property methods as follows:

@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

Which leads me to think that you would be able to access the synthesized ivar in your init* and dealloc methods as well. The only gotcha I could think of is that the @synthesize line may have to come before the definitions of your init* and dealloc methods in the source file.

In the end, since having the ivars declared in the interface still works, that is still your safest bet.

盗梦空间 2024-07-31 01:23:14

我遇到了同样的问题。 我解决无法访问合成实例变量的方法如下:

public header

@interface MyObject:NSObject {
}
@property (retain) id instanceVar;
@property (retain) id customizedVar;
@end

private header /implementation

@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 的依赖键。

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

@interface MyObject:NSObject {
}
@property (retain) id instanceVar;
@property (retain) id customizedVar;
@end

private header / implementation

@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

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.

℡寂寞咖啡 2024-07-31 01:23:14

我对 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...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文