【问题标题】:ObjC - Why is instance allowed to access class extension property in .m file?ObjC - 为什么允许实例访问 .m 文件中的类扩展属性?
【发布时间】:2019-06-03 23:14:52
【问题描述】:

众所周知,在.h文件中声明的ObjC属性是接口“外部可见”,而在.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自己的方法中定义的ClassA *aObj可以访问类扩展属性aInt,而且在同一个ClassA.m文件中定义在另一个_ClassB中的ClassA *aObj也可以访问aInt

据我了解,在类方法foo 中定义的aObj 与在另一个类和单独的.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 运行时机制什么的来解释吗?

【问题讨论】:

  • 因为objective-c 语言没有私有方法和属性。您只能在编译时通过隐藏它们的声明来模拟可访问性,但无论如何在运行时它们都是公开的。比如你可以调用私有api,苹果只能通过拒绝appstore中的分发来禁止它。

标签: ios objective-c properties class-extensions


【解决方案1】:

它与 Objective C 运行时无关。事实上,如果你使用键值编码,你可以从你想要的 any 源文件中访问 any 类中的 any 属性和/或方法,可以将其声明为私有或不声明,或者在扩展中或直接声明。这就是某些人(被禁止)使用 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 文件中的类扩展(匿名类别)中定义 iVars 或综合属性。见https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html

【讨论】:

  • 非常感谢您详细而清晰的回答!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-21
  • 1970-01-01
  • 1970-01-01
  • 2020-03-12
  • 1970-01-01
  • 2010-11-01
相关资源
最近更新 更多