1

众所周知,在 .h 文件中声明的 ObjC 属性是接口“外部可见”,而在 .m 文件中声明的属性(类扩展名)只能在 .m 中访问,即“私有”或“隐藏”。但实际上可以编译如下代码。

类A.h

@interface ClassA : NSObject
+ (void)foo;
@end

类A.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在类方法中定义与在另一个类和单独的 .m 文件中定义的foo任何类型变量没有区别。ClassA *但是后者绝不会访问'aInt',比如说

C类.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 运行时机制或其他什么来解释吗?

4

1 回答 1

1

它与 Objective C 运行时无关。事实上,如果你使用键值编码,你可以从任何你想要的源文件中访问任何类的任何属性和/或方法,无论它是否被声明为私有,或者在扩展中或直接。这就是某些人(被禁止)使用 Apple 私有 API 的方式。

Objective C 和 C 一样,只需要知道你的类的声明。这是通过导入头文件来完成的。头文件说“看,有类似的东西ClassA,它有这些方法和那些属性”,然后你就可以使用它们了。

.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时,编译器会知道类似的东西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 文件中的类扩展(匿名类别)中定义iVar或综合属性。请参阅https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html

于 2019-01-09T09:49:01.290 回答