斯威夫特:@objc(...) 属性

Posted

技术标签:

【中文标题】斯威夫特:@objc(...) 属性【英文标题】:Swift: @objc(...) Attribute 【发布时间】:2020-07-21 22:44:34 【问题描述】:

在 Apple 生成的代码中(例如,Core Data NSManagedObject 子类)我看到了这个:

@objc(LPFile)
public class LPFile: NSManagedObject 
   ...

我的问题是:为什么@objc 声明要像上面那样完成,而不是:

@objc public class LPFile: NSManagedObject 
   ...

@objcMembers public class LPFile: NSManagedObject 
   ...

单独的@objc(identifier) 声明有什么特别之处?我似乎找不到关于它的文档,而谷歌搜索只是发现了其他两种方法。谢谢。

(注意:我知道类前缀不是惯用的 Swift。)

【问题讨论】:

我有一种感觉,className 在一种情况下返回<module>.<name>(没有@objc(名称))而在另一种情况下只是<name> 起到了一些作用(也许是为了获得entityName (?))... 它仍应仅使用 @objc 进行编译。不是吗? @Sweeper 它应该,但它不会工作,因为 Objective-C 运行时类名称。我试图在下面的回答中解释它(希望可以理解)。 【参考方案1】:

@Alladinian 是对的。假设你有一个框架SharedSwift 有两个类:

@objc public class Foo: NSObject 
@objc(Bar) public class Bar: NSObject 

你可以在Objective-C代码中导入这个框架,直接使用这两个类:

@import SharedSwift;

Bar *b = [[Bar alloc] init];
Foo *f = [[Foo alloc] init];

但是因为 Objective-C 有一个强大的运行时,你可以做很多魔法。一个例子是NSClassFromString 函数:

- (id _Nullable)instanceByName:(NSString * _Nonnull)name 
    Class c = NSClassFromString(name);
    return [[c alloc] init];


Foo *foo = [self instanceByName:@"Foo"];
Bar *bar = [self instanceByName:@"Bar"];
    
NSLog(@"%@ %@", foo, bar);

输出是:

(null) <Bar: 0x6000015c4200>

有什么问题? className ...

NSLog(@"%@ %@", [Foo className], [Bar className]);

...在一种情况下返回SharedSwift.Foo (@objc),在另一种情况下返回Bar (@objc(Bar))。

SharedSwift.Foo Bar

让我们将AnotherSwift 框架添加到具有相同类的组合中,并尝试从两者中使用Foo

@import SharedSwift;

NSLog(@"%@", [Foo className]); // SharedSwift.Foo
@import AnotherSwift;

NSLog(@"%@", [Foo className]); // AnotherSwift.Foo

按预期工作。用Bar 类尝试同样的事情:

@import SharedSwift;

NSLog(@"%@", [Bar className]); // Bar
@import AnotherSwift;

NSLog(@"%@", [Bar className]); // Bar

Bar 类在两个框架中都定义了,将使用哪一个是未定义的。尝试此操作时在控制台中查看错误:

Class Bar is implemented in both
.../Debug/SharedSwift.framework/Versions/A/SharedSwift (0x102b931c0) and
.../Debug/AnotherSwift.framework/Versions/A/AnotherSwift (0x102b841c0).
One of the two will be used. Which one is undefined.

这是什么原因?

如您所见,Objective-C 代码(@import SharedSwift 和直接使用 Foo)和 Objective-C 运行时名称(NSClassFromString,...)之间存在差异。

Objective-C 世界中的所有事物都有一个命名空间。这就是 Apple 框架中这两个字母前缀(NSUICF、...)和 3rd 方代码中的三个字母前缀的原因。一些第 3 方开发者仍然使用两个字母,但那是另一回事了。

Swift 有更多的命名空间——它们基于模块。当使用纯 @objc 属性时,包含模块名称是一个安全的选择。避免可能的歧义。

检查 NSEntityDescription 类例如 - managedObjectClassName 属性:

代表接收者实体的类的名称。

有很多东西利用了 Objective-C 运行时特性,很多东西只是基于名称(字符串),...

【讨论】:

等待这是否允许将 swift 类融合到一个 objc 类中,除了歧义风险和“将使用两个中的一个。哪个未定义。” ? 优秀而彻底的答案。作为一名拥有数十年历史的 Objective-C 开发人员,很高兴看到仍然存在了解该语言基础的人。

以上是关于斯威夫特:@objc(...) 属性的主要内容,如果未能解决你的问题,请参考以下文章

斯威夫特:这个错误:'private(set)'修饰符不能应用于只读属性是啥意思?

objc:具有 NSString 的强属性

核心数据 - 某些属性崩溃:objc_release

斯威夫特,演员:演员隔离属性“扫描”不能从非隔离上下文中变异

objc runtime 动态增加属性-备用(?)

ObjC 属性和 C 运算符