ma1*_*w28 7 properties private objective-c instance-variables
我已经读过,@synthesize它将自动创建相应的实例变量,@property@protected默认情况下为 ivars .但是,如果我使用类扩展(如下所示)来指示@property方法是私有的呢?
// Photo.m
@interface Photo ()
@property (nonatomic, retain) NSMutableData *urlData;
@end
Run Code Online (Sandbox Code Playgroud)
相应的ivar会是@private吗?或者我应该明确地声明它是@private这样的吗?
// Photo.h
@interface Photo : Resource {
@private
NSMutableData *urlData;
}
Run Code Online (Sandbox Code Playgroud)
小智 31
对Kevin的回答进行了阐述:
当您声明一个类时,例如:
@interface SomeClass : NSObject {
@public
id publicIvar;
@protected
id protectedIvar;
@private
id privateIvar;
}
@end
Run Code Online (Sandbox Code Playgroud)
编译器1决定该类的实例变量布局.此布局确定实例变量的偏移量,该偏移量与该类的实例地址有关.一种可能的布局是:
+--> publicIvar address = instance address + offsetOfPublicIvar
|
|
+-----+------------+-----+---------------+-----+-------------+-----+
| ... | publicIvar | ... | protectedIvar | ... | privateIvar | ... |
+-----+------------+-----+---------------+-----+-------------+-----+
|
|
+--> instance address
Run Code Online (Sandbox Code Playgroud)
当在代码中引用实例变量时 - 无论是在类的实现中还是在代码库的某个其他部分中,编译器都会将该引用替换为实例变量的相应偏移量,而不是相应实例的地址.
例如,在SomeClass的实现中,
privateIvar = someObject;
Run Code Online (Sandbox Code Playgroud)
要么
self->privateIvar = someValue;
Run Code Online (Sandbox Code Playgroud)
被翻译为:
*(self + offsetOfPrivateIvar) = someObject;
Run Code Online (Sandbox Code Playgroud)
同样,在课堂之外,
SomeClass *obj = [SomeClass new];
obj->publicIvar = someObject;
Run Code Online (Sandbox Code Playgroud)
被翻译为:
SomeClass *obj = [SomeClass new];
*(obj + offsetOfPublicIvar) = someObject;
Run Code Online (Sandbox Code Playgroud)
但是,编译器只允许根据实例变量的可见性:
在类扩展中声明实例变量时,例如
@interface SomeClass () {
id extensionIvar;
}
@end
Run Code Online (Sandbox Code Playgroud)
编译器将它添加到实例变量布局:
+-----+------------+-----+---------------+
| ... | otherIvars | ... | extensionIvar |
+-----+------------+-----+---------------+
Run Code Online (Sandbox Code Playgroud)
并且对该实例变量的任何引用都将替换为与实例相关的相应偏移量.但是,由于该实例变量仅对已声明类扩展的实现文件已知,因此编译器不允许其他文件引用它.任意源文件只能引用它知道的实例变量(遵守可见性规则).如果实例变量是在源文件导入的头文件中声明的,那么源文件(或者更准确地说,是翻译该单元时的编译器)就会知道它们.
另一方面,扩展变量仅由声明它的源文件所知.因此,我们可以说在类扩展中声明的实例变量对其他文件是隐藏的.相同的推理适用于支持类扩展中声明的属性的实例变量.它类似@private,但更具限制性.
但请注意,在运行时不会强制执行可见性规则.使用键值编码,有时(这里描述的规则)任意源文件可以访问实例变量:
SomeClass *obj = [SomeClass new];
id privateValue = [obj valueForKey:@"privateIvar"];
Run Code Online (Sandbox Code Playgroud)
包括在扩展中声明的实例变量:
id extensionValue = [obj valueForKey:@"extensionIvar"];
Run Code Online (Sandbox Code Playgroud)
无论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);
Run Code Online (Sandbox Code Playgroud)
请注意,一个类可以有多个类扩展.但是,一个类扩展不能声明与另一个实例变量具有相同名称的实例变量,包括在其他类扩展中声明的实例变量.由于编译器发出如下符号:
_OBJC_IVAR_$_SomeClass.extensionIvar
Run Code Online (Sandbox Code Playgroud)
对于每个实例变量,具有不同的扩展名,声明具有相同名称的实例变量不会产生编译器错误,因为给定的源文件不知道另一个源文件,但它确实会产生链接器错误.
1可以通过Objective-C运行时更改此布局.实际上,偏移量由编译器计算并存储为变量,运行时可以根据需要更改它们.
PS:这个答案中的所有内容都不适用于所有编译器/运行时版本.我只考虑了使用非脆弱ABI和最近版本的Clang/LLVM的Objective-C 2.0.
Lil*_*ard 12
@private实例变量是仅编译时功能.鉴于@property已经隐藏了对iv的支持,@private没有做任何事情.所以从本质上讲,它已经存在了@private.
| 归档时间: |
|
| 查看次数: |
2646 次 |
| 最近记录: |