私有@property是否创建@private实例变量?
我读过 @synthesize
会自动为@property
创建相应的实例变量,并且ivars默认为@protected
。但是,如果我使用类扩展(如下所示)来指示 @property
方法是私有的,该怎么办?
// Photo.m
@interface Photo ()
@property (nonatomic, retain) NSMutableData *urlData;
@end
那么对应的ivar会是@private
吗?或者我应该像这样明确地将其声明为 @private
?
// Photo.h
@interface Photo : Resource {
@private
NSMutableData *urlData;
}
I've read that @synthesize
will automatically create corresponding instance variables for @property
and that ivars are @protected
by default. But, what if I use a class extension (like below) to indicate that the @property
methods are to be private?
// Photo.m
@interface Photo ()
@property (nonatomic, retain) NSMutableData *urlData;
@end
Will the corresponding ivar then be @private
? Or should I explicitly declare it as @private
like so?
// Photo.h
@interface Photo : Resource {
@private
NSMutableData *urlData;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
详细说明凯文的答案:
当您声明一个类时,例如:
编译器1决定该类的实例变量布局。此布局确定实例变量相对于该类实例的地址的偏移量。一种可能的布局是:
当在代码中引用实例变量时 - 无论是在类的实现中还是在代码库的其他部分中,编译器都会将该引用替换为实例变量相对于地址的相应偏移量对应的实例。
例如,在 SomeClass 的实现中,
or
被翻译成这样:
同样,在类之外,
被翻译成这样:
但是,编译器只根据实例变量的可见性允许这样做:
当在类扩展中声明实例变量时,例如,
编译器将其添加到实例变量布局中:
并且对该实例变量的任何引用都将替换为其相对于该实例的相应偏移量。但是,由于该实例变量只有声明了类扩展的实现文件才知道,因此编译器不允许其他文件引用它。任意源文件只能引用它知道的实例变量(遵守可见性规则)。如果实例变量在由源文件导入的头文件中声明,则源文件(或更准确地说,翻译该单元时的编译器)会识别它们。
另一方面,扩展变量只有声明它的源文件才知道。因此,我们可以说在类扩展中声明的实例变量对其他文件是隐藏的。同样的推理也适用于类扩展中声明的属性的支持实例变量。它类似于
@private
,但限制更多。但请注意,运行时可见性规则不会强制执行。使用键值编码,有时可以使用任意源文件(规则描述此处) 访问实例变量:
包括在扩展中声明的实例变量:
无论 KVC 如何,对实例变量的访问都可以通过 Objective-C 运行时 API 完成:
请注意,类可以有多个类扩展。但是,一个类扩展不能声明与另一实例变量同名的实例变量,包括在其他类扩展中声明的实例变量。由于编译器发出如下符号:
对于每个实例变量,使用不同的扩展名声明具有相同名称的实例变量不会产生编译器错误,因为给定的源文件不知道另一个源文件,但它确实会产生链接器错误。
1此布局可以由 Objective-C 运行时更改。事实上,偏移量由编译器计算并存储为变量,运行时可以根据需要更改它们。
PS:此答案中的所有内容并不适用于所有编译器/运行时版本。我只考虑过具有非脆弱 ABI 的 Objective-C 2.0 和最新版本的 Clang/LLVM。
Ellaborating on Kevin’s answer:
When you declare a class, e.g.:
the compiler1 decides upon an instance variable layout for that class. This layout determines offsets of instance variables with regard to the address of instances of that class. One possible layout would be:
When an instance variable is referenced in code — either in the implementation of the class or in some other part of the code base, the compiler replaces that reference with the corresponding offset of the instance variable with regard to the address of the corresponding instance.
For example, in the implementation of SomeClass,
or
is translated as something like:
Similarly, outside of the class,
is translated as something like:
However, the compiler only allows this according to the visibility of the instance variable:
When an instance variable is declared in a class extension, e.g.
the compiler adds it to the instance variable layout:
and any reference to that instance variable is replaced by its corresponding offset with regard to the instance. However, since that instance variable is only known to the implementation file where the class extension has been declared, the compiler won’t allow other files to reference it. An arbitrary source file can only reference instance variables it knows (respecting the visibility rules). If instance variables are declared in a header file that’s imported by a source file, then the source file (or, more accurately, the compiler whilst translating that unit) is aware of them.
On the other hand, an extension variable is only known by the source file where it was declared. We can thus say that instance variables declared in class extensions are hidden from other files. The same reasoning applies to backing instance variables of properties declared in class extensions. It’s similar to
@private
, but more restrictive.Note, however, that at runtime visibility rules are not enforced. Using Key-Value Coding, an arbitrary source file can sometimes (the rules are described here) access an instance variable:
including an instance variable declared in an extension:
Regardless of KVC, access to instance variables be accomplished via the Objective-C runtime API:
Note that a class can have more than one class extension. However, one class extension cannot declare an instance variable with the same name as another instance variable, including instance variables declared in other class extensions. Since the compiler emits symbols like:
for each instance variable, having different extensions declaring instance variables with the same name doesn’t yield a compiler error because a given source file is not aware of another source file, but it does yield a linker error.
1This layout can be changed by the Objective-C runtime. In fact, offsets are computed by the compiler and stored as variables, and the runtime can change them as needed.
PS: Not everything in this answer applies to all compiler/runtime versions. I’ve only considered Objective-C 2.0 with non-fragile ABI and recent versions of Clang/LLVM.
@private
实例变量是仅编译时功能。鉴于@property
的支持 ivars 已经隐藏,@private
不会执行任何操作。所以本质上,它已经是@private
了。@private
instance variables are a compile-time-only feature. Given that the backing ivars for@property
's are already hidden,@private
doesn't do anything. So in essence, it's already@private
.