ObjC - 为什么允许实例访问 .m 文件中的 class 扩展名 属性?
ObjC - Why is instance allowed to access class extension property in .m file?
众所周知,在 ObjC 中,.h 文件中声明的 属性 是接口 "visible outside",而 .m 文件(class 扩展名)中声明的 属性 可以只能在 .m 中访问,类似于 "private" 或 "hidden"。但实际上可以编译出如下代码
ClassA.h
@interface ClassA : NSObject
+ (void)foo;
@end
ClassA.m
#import "ClassA.h"
@interface ClassA ()
@property (nonatomic) NSInteger aInt;
@end
@implementation ClassA
+ (void)foo {
ClassA *aObj = [ClassA new];
aObj.aInt = 2; //?
}
@end
@interface _ClassB : NSObject //Some private class defined in the same .m file...
@end
@implementation _ClassB
+ (void)bar {
ClassA* aObj = [ClassA new];
aObj.aInt = 2; //?
}
@end
事实是,不仅ClassA
自己的方法中定义的ClassA *aObj
可以访问class扩展属性aInt
,ClassA *aObj
在另一个 _ClassB
中定义,而在同一个 ClassA.m 文件中也可以访问 aInt
。
据我了解,class 方法 foo
中定义的 aObj
与另一个 class 中定义的任何 ClassA *
类型变量没有区别,并且单独的 .m 文件。但绝不是后者会访问'aInt',说
ClassC.m
#import "ClassA.h"
...
- (void)fun {
ClassA *aObj = [ClassA new];
NSLog("%d", aObj.aInt); //Error! Property aInt not found on object of type 'ClassA*'
}
为什么会这样?这可以用 ObjC 运行时机制或其他东西来解释吗?
与Objective C运行时无关。事实上,如果你使用键值编码,你可以访问 any 属性 and/or 方法从 any class 来自 任何 你想要的源文件,它可以声明为私有或不私有,或者在扩展中或直接声明。这就是某些人(禁止)使用 Apple 私有 API 的方式。
Objective C,像 C 一样,只需要 知道 你的 class 的声明。这是通过导入头文件来完成的。头文件说的是"Look, there is something like ClassA
, it has these methods and those properties",然后就可以用了
在 .m 文件中声明的任何内容对其他源文件都是不可见的,因为您通常不会导入 .m 文件(尽管从技术上讲,它可以工作)。尽管如此,声明仍然存在 - 只是编译器在编译其他文件时不知道它。
您可以创建一个虚拟头文件:
// FakeClassAExtension.h
// ...
@interface ClassA (Fake)
@property (nonatomic) NSInteger aInt;
@end
然后在您的 ClassC
:
中使用它
// ClassC.m
#import "ClassA.h"
#import "FakeClassAExtension.h"
//...
- (void)fun {
ClassA *aObj = [ClassA new];
NSLog("%d", aObj.aInt); //Fine
}
编译 ClassC.m
时,编译器会知道 ClassA
中存在类似 aInt
的内容。链接器 - 作为最后一步 - 然后检查这是否真的是真的,例如如果一个(且只有一个)编译后的源文件包含 aInt
.
的定义
试试这个:只需 声明 一个 属性 未在任何地方 定义:
// FakeClassAExtension2.h
// ...
@interface ClassA (Fake2)
@property (nonatomic) NSInteger oopsDoesItExist;
@end
然后使用它:
// ClassC.m
#import "ClassA.h"
#import "FakeClassAExtension2.h"
//...
- (void)fun {
ClassA *aObj = [ClassA new];
NSLog("%d", aObj.oopsDoesItExist); //Compiler is fine here
}
编译器会编译代码,但是链接器会说没有 definition of oopsDoesItExist
最后一点:您只能在 .m 文件中的 class 扩展名(匿名类别)中定义 iVars 或综合属性。参见 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html
众所周知,在 ObjC 中,.h 文件中声明的 属性 是接口 "visible outside",而 .m 文件(class 扩展名)中声明的 属性 可以只能在 .m 中访问,类似于 "private" 或 "hidden"。但实际上可以编译出如下代码
ClassA.h
@interface ClassA : NSObject
+ (void)foo;
@end
ClassA.m
#import "ClassA.h"
@interface ClassA ()
@property (nonatomic) NSInteger aInt;
@end
@implementation ClassA
+ (void)foo {
ClassA *aObj = [ClassA new];
aObj.aInt = 2; //?
}
@end
@interface _ClassB : NSObject //Some private class defined in the same .m file...
@end
@implementation _ClassB
+ (void)bar {
ClassA* aObj = [ClassA new];
aObj.aInt = 2; //?
}
@end
事实是,不仅ClassA
自己的方法中定义的ClassA *aObj
可以访问class扩展属性aInt
,ClassA *aObj
在另一个 _ClassB
中定义,而在同一个 ClassA.m 文件中也可以访问 aInt
。
据我了解,class 方法 foo
中定义的 aObj
与另一个 class 中定义的任何 ClassA *
类型变量没有区别,并且单独的 .m 文件。但绝不是后者会访问'aInt',说
ClassC.m
#import "ClassA.h"
...
- (void)fun {
ClassA *aObj = [ClassA new];
NSLog("%d", aObj.aInt); //Error! Property aInt not found on object of type 'ClassA*'
}
为什么会这样?这可以用 ObjC 运行时机制或其他东西来解释吗?
与Objective C运行时无关。事实上,如果你使用键值编码,你可以访问 any 属性 and/or 方法从 any class 来自 任何 你想要的源文件,它可以声明为私有或不私有,或者在扩展中或直接声明。这就是某些人(禁止)使用 Apple 私有 API 的方式。
Objective C,像 C 一样,只需要 知道 你的 class 的声明。这是通过导入头文件来完成的。头文件说的是"Look, there is something like ClassA
, it has these methods and those properties",然后就可以用了
在 .m 文件中声明的任何内容对其他源文件都是不可见的,因为您通常不会导入 .m 文件(尽管从技术上讲,它可以工作)。尽管如此,声明仍然存在 - 只是编译器在编译其他文件时不知道它。
您可以创建一个虚拟头文件:
// FakeClassAExtension.h
// ...
@interface ClassA (Fake)
@property (nonatomic) NSInteger aInt;
@end
然后在您的 ClassC
:
// ClassC.m
#import "ClassA.h"
#import "FakeClassAExtension.h"
//...
- (void)fun {
ClassA *aObj = [ClassA new];
NSLog("%d", aObj.aInt); //Fine
}
编译 ClassC.m
时,编译器会知道 ClassA
中存在类似 aInt
的内容。链接器 - 作为最后一步 - 然后检查这是否真的是真的,例如如果一个(且只有一个)编译后的源文件包含 aInt
.
试试这个:只需 声明 一个 属性 未在任何地方 定义:
// FakeClassAExtension2.h
// ...
@interface ClassA (Fake2)
@property (nonatomic) NSInteger oopsDoesItExist;
@end
然后使用它:
// ClassC.m
#import "ClassA.h"
#import "FakeClassAExtension2.h"
//...
- (void)fun {
ClassA *aObj = [ClassA new];
NSLog("%d", aObj.oopsDoesItExist); //Compiler is fine here
}
编译器会编译代码,但是链接器会说没有 definition of oopsDoesItExist
最后一点:您只能在 .m 文件中的 class 扩展名(匿名类别)中定义 iVars 或综合属性。参见 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html