众所周知,在ObjC中,在.h文件中声明的属性是“可见外部”接口,而在.m文件(类扩展名)中声明的属性只能在.m中访问,即“私有”或“隐藏” ”。但实际上,可以编译以下代码。
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 *aObj
自己的方法中定义的ClassA
可以访问类扩展属性aInt
,而且另一个ClassA *aObj
中定义的_ClassB
也可以访问而在同一ClassA.m文件中,也可以访问aInt
。
据我了解,在类方法aObj
中定义的foo
与在另一个类和单独的.m文件中定义的任何ClassA *
类型变量没有区别。但是,后者绝对不会访问“ 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运行时机制或其他方式解释吗?
答案 0 :(得分:1)
与Objective C运行时无关。实际上,如果您使用键值编码,则可以从所需的 any 源文件的 any 类访问 any 属性和/或方法,可以将其声明为私有,也可以不声明为私有,也可以直接声明为扩展名。这就是某些人(禁止)使用Apple的私有API的方式。
与C一样,目标C只需知道您的类的声明。这是通过导入头文件来完成的。头文件显示“看起来像ClassA
,它具有这些方法和那些属性”,然后就可以使用它们。
.m文件中声明的所有内容对于其他源文件都不可见,因为您通常不导入.m文件(尽管从技术上讲,它可以工作)。但是,该声明仍然 exist 存在-只是编译器在编译其他文件时不知道该声明。
您可以创建一个虚拟头文件:
// 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
时,编译器会知道aInt
中存在ClassA
之类的东西。链接器-作为最后一步-然后检查这是否确实正确,例如如果一个(只有一个)已编译的源文件包含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
}
编译器将编译代码,但是链接器将说没有oopsDoesItExist
最后一点:您只能在.m文件中的类扩展名(匿名类别)中定义 iVars 或合成属性。参见https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html