在Objective-C中,为什么类继续可以添加实例变量但类类不能?

bil*_*990 4 objective-c

我知道我们不能在类类别中添加实例变量,但是我们可以添加类继续,可以有人告诉我为什么或给我一些关于它的细节.我知道我们可以在类别中使用关联,我想知道有关实现的详细信息.非常感谢 .

Gab*_*lla 6

TL;博士

类扩展仅修改您拥有的类,而类可以修改任何现有类.

将ivars添加到现有类可能会破坏旧运行时(iOS或OSX 64位之前)的二进制兼容性,这就是为什么被禁止的原因.

添加方法不会遇到此问题,因为方法解析始终是动态的.


讨论

因为类扩展(clang称为类连续)不是类别,尽管它们有一些共同点.

来自Apple文档

类扩展与类别有一些相似之处,但它只能添加到您在编译时拥有源代码的类(该类与类扩展同时编译).类扩展声明的方法是@implementation原始类的块中实现的,因此您不能在框架类上声明类扩展,例如Cocoa或Cocoa Touch类,如NSString.

类扩展在编译时扩展了类的接口:它是代码组织和可见性的简单问题(例如,声明私有属性很有用).

您只是将@interface声明分成多个部分,但是您不能向现有的类添加任何您不拥有的内容.

由于这个原因,自然支持添加ivar.大概你可以这样做:

@interface MyClass : NSObject {
    NSInteger a;
    NSInteger b;
}
@end
Run Code Online (Sandbox Code Playgroud)

要么

@interface MyClass : NSObject {
    NSInteger a;
}
@end

@interface MyClass() {
    NSInteger b;
}
@end
Run Code Online (Sandbox Code Playgroud)

几乎是等价的(如果你将类扩展放在一个私有的.m文件中,那么b它将是私有的).

另一方面,对于类类别,您可以修改任何类.

这是至关重要的,因为在设计类别时,Objective-C具有脆弱的基础ivars.

这是什么意思?这意味着一旦编译了一个类,ivars布局就永远固定在二进制文件中,如果不破坏与类的任何子类的二进制兼容性,就不能修改它.

因此,添加一个ivar,比如说,NSObject会破坏(几乎)每个编译过的框架中的每个类.

相反,方法是动态解析的,因此只要您没有名称冲突,就可以安全地将方法添加到现有类中.

自从第一个iOS(当时的iPhone OS)和使用动态ivars的64位OSX以来,脆弱的ivar问题得到了解决,但Objective-C无法在不破坏与32位Mac的兼容性的情况下利用此功能.


参考

http://www.sealiesoftware.com/blog/archive/2009/01/27/objc_explain_Non-fragile_ivars.html http://www.cocoawithlove.com/2010/03/dynamic-ivars-solving-fragile-base.html