将实例变量与Modern Runtime一起使用

时间:2009-06-17 15:01:26

标签: objective-c macos objective-c-runtime

我在Obj-c和Cocoa有几年的经验,但我刚刚回到它和Obj-C 2.0等的进步。

我正试图绕过现代运行时并声明属性等。让我感到困惑的一件事是现代运行时能够隐式创建iVar。当然,这意味着您应该始终使用self.property来访问该值。

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

所以问题是:

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

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

  3. 如果我需要覆盖一个访问器,我是否仍然可以访问将在运行时定义的iVar,或者我是否必须定义运行时将使用的实际iVar?

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

  5. 我多次阅读文档,但是他们对所有这些看起来有点模糊,我想确保我理解它以便决定如何继续编码。

    希望我的问题清楚。


    测试快速摘要

    1. 如果您没有在遗产中声明ivar,编译器完全不满意

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

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

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

    5. 当然,
    6. @private宣告ivar可以直接访问传统和现代的ivar

    7. 现在没有真正给出一个干净的方法吗?

5 个答案:

答案 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是   一般都足够好。

因此,现在您必须使用点符号来访问属性,即使在initdealloc中也是如此。这违背了在这些情况下直接使用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的手动规则。

当然还有很多其他我也做错的事情......