2011-04-26 45 views
7

我读过@synthesize will automatically create corresponding instance variables for @property,默认情况下ivars是@protected。但是,如果我使用类扩展(如下所示)来表示@property方法是私有的,那该怎么办?私有@property是否创建@private实例变量?

// Photo.m 
@interface Photo() 
@property (nonatomic, retain) NSMutableData *urlData; 
@end 

请问对应的伊娃是@private?或者我应该明确声明它是这样的@private

// Photo.h 
@interface Photo : Resource { 
@private 
    NSMutableData *urlData; 
} 

回答

12

@private实例变量是仅编译时功能。鉴于@property的支持ivars已被隐藏,@private不会执行任何操作。所以实质上它已经是@private

+7

....并没有“匿名类别”这样的东西。这将是OP的问题中的“类延伸”与一个类别非常不同的野兽(虽然功能相似)。 – bbum 2011-04-26 03:45:05

+0

@bbum,谢谢,修复! – ma11hew28 2011-06-07 21:50:21

31

Ellaborating对凯文的回答是:

当你声明一个类,例如:

@interface SomeClass : NSObject { 
@public 
    id publicIvar; 
@protected 
    id protectedIvar; 
@private 
    id privateIvar; 
} 
@end 

编译决定为该类的实例变量的布局。此布局确定实例变量与该类实例地址的偏移量。一种可能的布局是:

 +--> publicIvar address = instance address + offsetOfPublicIvar 
     | 
     | 
+-----+------------+-----+---------------+-----+-------------+-----+ 
| ... | publicIvar | ... | protectedIvar | ... | privateIvar | ... | 
+-----+------------+-----+---------------+-----+-------------+-----+ 
| 
| 
+--> instance address 

当一个实例变量是在代码中引用 - 无论是在类的,或在代码库的一些其他部分的实施方式中,编译器替换为相应的偏移量的该参考实例变量关于相应实例的地址。

例如,在SomeClass的执行,

privateIvar = someObject; 

self->privateIvar = someValue; 

被翻译为这样的:

*(self + offsetOfPrivateIvar) = someObject; 

类似地,类以外,

SomeClass *obj = [SomeClass new]; 
obj->publicIvar = someObject; 

被翻译为这样的:

SomeClass *obj = [SomeClass new]; 
*(obj + offsetOfPublicIvar) = someObject; 

然而,编译器只允许这个根据实例变量的可见性:

  • 私有的实例变量可以在实现仅引用相应的班级;
  • 受保护的实例变量只能在相应的类及其子类的实现中引用;
  • 公共实例变量可以在任何地方引用。

当在类扩展中声明一个实例变量时,例如

@interface SomeClass() { 
    id extensionIvar; 
} 
@end 

编译器将其添加到实例变量布局:

+-----+------------+-----+---------------+ 
| ... | otherIvars | ... | extensionIvar | 
+-----+------------+-----+---------------+ 

和该实例变量的任何参考文献通过其相应关于实例偏移替换。但是,由于该实例变量仅在声明了类扩展名的实现文件中是已知的,因此编译器将不允许其他文件引用它。一个任意的源文件只能引用它知道的实例变量(尊重可见性规则)。如果实例变量在由源文件导入的头文件中声明,那么源文件(或者,更准确地说,编译器在翻译该单元时)知道它们。

另一方面,一个扩展变量只被声明的源文件所知。因此,我们可以说在类扩展中声明的实例变量对其他文件是隐藏的。同样的推理适用于在类扩展中声明的属性的背景实例变量。它与@private类似,但更具限制性。

但是请注意,在运行时可见性规则不会被强制执行。使用键 - 值编码,一个任意的源文件有时(规则被描述here)访问一个实例变量:

SomeClass *obj = [SomeClass new]; 
id privateValue = [obj valueForKey:@"privateIvar"]; 

包括在一个扩展声明的实例变量:

id extensionValue = [obj valueForKey:@"extensionIvar"]; 

不管KVC的通过Objective-C运行时API可以访问实例变量:

Ivar privateIvar = class_getInstanceVariable([SomeClass class], 
              "privateIvar"); 
Ivar extensionIvar = class_getInstanceVariable([SomeClass class], 
               "extensionIvar"); 

id privateValue = object_getIvar(obj, privateIvar); 
id extensionValue = object_getIvar(obj, extensionIvar); 

请注意,一个类可以有多个类extensi上。但是,一个类扩展名不能声明与另一个实例变量具有相同名称的实例变量,包括在其他类扩展中声明的实例变量。由于编译器发射符号喜欢:

_OBJC_IVAR_$_SomeClass.extensionIvar 

为每个实例变量,具有声明实例变量具有相同的名称,因为给定的源文件是不知道另一源文件的不产生一个编译器错误不同的扩展,但它确实会产生链接器错误。

此布局可以通过Objective-C运行时更改。实际上,偏移量由编译器计算并存储为变量,并且运行时可根据需要更改它们。

PS:并非此答案中的所有内容都适用于所有编译器/运行时版本。我只考虑Objective-C 2.0与非脆弱ABI和最近版本的Clang/LLVM。

+0

虽然这些类型的评论是不鼓励的:*谢谢你这个非常丰富,简洁和开眼界的答案*。 – Madbreaks 2013-01-09 20:22:22

相关问题