我知道现代的Objective-C运行时可以合成ivars。我认为合成的ivars的行为与标记为@private
的声明的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
子类不能具有声明的ivar,其名称与其超类的声明的ivar同名,即使超类的ivar是私有的。这似乎违反了@private
的含义,因为子类受到超类选择私有的影响。
然而,我更关心的是我应该如何看待合成的伊娃。我认为他们的行为就像宣布的私人伊娃,但没有脆弱的基类问题。也许这是正确的,我只是不明白脆弱的基类问题。为什么上面的代码只在现代运行时编译?当所有超类实例变量都是私有的时,是否存在脆弱的基类问题?
答案 0 :(得分:6)
合成的ivars 是私有的。你所看到的是编译器正常工作。
忽略!__OBJ2__
条件中的代码。我只会看一下合成的ivars案例。
这是您的代码所具有的:
A::_c
是合成的ivar,是
只能在
实施A
。B::_c
是一个
合成ivar并且是唯一的
在实施中可访问
B
。它们不是同一个变量。它们不会发生冲突,也不会存储相同的值。
仍然可能出现问题的情况......
如果您尝试将A
和B
的实现放在同一个文件中,编译器现在可以在编译时看到A::_c
的声明B 并阻止您访问_c
中@synthesize e=_c;
行中的B
。
为什么要这样做?我不是说A::_c
和B::_c
是分开的,不相关的变量吗?
将两个实现放在同一个文件中会导致当编译器到达_c
时@synthesize e=_c;
不是未声明的标识符,因此编译器不会尝试为{{1}创建新的合成ivar相反,它尝试访问B
并失败(因为A::_c
是私有的)。
答案 1 :(得分:2)
简而言之,你不能[拥有私人ivars]。
另一种方法是在实现文件中声明一个包含所有状态的类,然后将其视为结构。
的.m:
@interface PrivateGoo:NSObject
{... ivars ...}
... props
@end
@implementation PrivateGoo
... @synth or not
然后,在.h中,声明像id privateGoop;
这样的ivar。如果查找速度很可怕,请在.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保留了传统行为。