ObjC-为什么实例被允许访问.m文件中的类扩展属性?

时间:2019-01-09 08:16:48

标签: ios objective-c properties class-extensions

众所周知,在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运行时机制或其他方式解释吗?

1 个答案:

答案 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