2010-03-21 88 views
1

我知道现代Objective-C运行时可以合成ivars。我认为合成的ivars的行为与标记为@private的声明的ivars完全相同,但它们没有。声明的私有ivars与合成的ivars有什么不同?

因此,只有在我期望可以工作的现代运行时才能编译代码。例如,超:

@interface A : NSObject { 
#if !__OBJC2__ 
    @private 
    NSString *_c; 
#endif 
} 

@property (nonatomic, copy) NSString *d; 

@end 


@implementation A 

@synthesize d=_c; 

- (void)dealloc { 
    [_c release]; 
    [super dealloc]; 
} 

@end 

和子类:

@interface B : A { 
#if !__OBJC2__ 
@private 
    NSString *_c; 
#endif 
} 

@property (nonatomic, copy) NSString *e; 

@end 


@implementation B 

@synthesize e=_c; 

- (void)dealloc { 
    [_c release]; 
    [super dealloc]; 
} 

@end 

子类不能有相同名称的声明伊娃作为其超类的一个声明实例变量,即使父类的伊娃是私人的。在我看来,这似乎违反了@private的含义,因为子类受到超类对隐私的选择的影响。

然而,我更关心的是我应该如何考虑合成ivars。我认为他们表现得像宣布的私人ivars,但没有脆弱的基础类问题。也许这是对的,我只是不理解脆弱的基类问题。为什么上述代码仅在现代运行时中编译?当所有超类实例变量都是私有的时候,脆弱的基类问题是否存在?

回答

6

合成ivars 私人。你看到的是编译器正常工作。

忽略!__OBJ2__条件中的代码。我只会看看合成的ivars案例。

这是你的代码有:

  • A::_c是合成的伊娃,是 只有 实施A中访问。
  • B::_c是合成伊法尔的 ,并且在B的实施 内仅可获得 。

它们不是同一个变量。它们不会相互碰撞,也不会存储相同的值。

情况下仍然会出现一个问题...

如果你试图把AB的在同一个文件执行,编译器现在能够看到的A::_c声明同时正在编译B并且将阻止您访问B@synthesize e=_c;行中的_c

为什么要这样做?我不是说A::_cB::_c是单独的,无关的变量吗?

在同一文件中有两个实现产生了一种情况_c不是未声明的标识符时,编译器达到@synthesize e=_c;,所以编译器不会尝试创建一个新的综合类伊娃的B,而是试图访问A::_c和失败(因为A::_c是私人的)。

+0

啊,所以在声明private private ivars时会出现同样的问题:'B.h'导入'A.h',声明'_c'。随后对“_c”的引用将被解释为对“A :: _ c”的引用,而不是未声明的标识符。这是有道理的,尽管它似乎仍然是错误的。我希望符号的范围仅限于超类,并且它将被视为在其他地方未声明。 – lemnar 2010-03-22 06:37:32

2

总之,你不能[有私人ivars]。

另一种方法是在包含所有状态的实现文件中声明一个类,然后将其视为结构。

的.m:

@interface PrivateGoo:NSObject 
{... ivars ...} 
... props 
@end 

@implementation PrivateGoo 
... @synth or not 

然后,在.H,声明一个实例变量等id privateGoop;。如果查找速度很糟糕,请在.m文件中使用((PrivateGoo*)privateGoop)->iVar;。如果你这样做,要小心内存管理。

请注意,这有几个优点,即使它乍看起来似乎很奇怪。也就是说,它使得重构PrivateGoo琐碎;如果你的数据封装突然发现需要有业务逻辑或更普遍可用,那么这样做是微不足道的。使用GC,编译器将为PrivateGoo制定布局信息,并将完全自动管理所有内存。使用结构需要箍跳跃,使工作权的略带一点 - 正确的分配模式等


对不起 - 实际上并没有回答这个问题。

合成ivars的工作方式与普通ivars非常相似,只是您不必在头文件中声明'em'。除此之外,对他们没有任何特别的私密性。请注意,合成的ivars不需要避免脆弱的基类问题。

@private,@protected@public指令为编译器的利益而存在;当你访问被声明为不可访问的东西时,给它一个警告(或错误)的钩子。仍然有OBjective-C的相对平坦的名称空间可以满足,这就是阻止你在子类中拥有同名的iVar的原因。

脆弱的基类问题与属性和合成的ivars是正交的。在iPhone OS和64位Mac OS X上的现代Objective-C 2.0 ABI中讨论了易碎基类。出于二进制兼容性原因,32位Mac OS X保留了传统行为。

+0

我不能做什么?我没有试图避免公开头文件中公开头文件的实现细节,也没有解决遗留运行库中的脆弱基类问题。我试图避免与私有超类实例变量的名称空间冲突。子类无法访问私有ivars。但是,子类不能拥有与其超类相同名称的私有ivar。由于上述代码适用于现代运行时,合成的ivars确实允许这样做。我认为代码在两次运行时都会表现相同。这是'@ private'中的错误吗?我不假设。为什么不? – lemnar 2010-03-21 19:22:32

+0

我想我现在开始明白了:我认为'@ private'做了一些更复杂的事情,比如实际上隐藏了其他类的变量。不过,我仍然对为什么我的代码在现代运行时下工作感到困惑。我的亚类不应该禁止合成与超级合成伊娃相同名称的伊娃吗?相反,它似乎是一个真正的私人(隐藏)伊娃。 – lemnar 2010-03-22 00:13:32

+1

编译器禁止合成存储位于超类中的属性。因此,允许在超级和子类中合成具有相同名称的ivars。 – bbum 2010-03-22 16:16:53