首页 > 解决方案 > 斯威夫特:@objc(...) 属性

问题描述

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

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

我的问题是:为什么@objc要按上述方式进行声明,而不是:

@objc public class LPFile: NSManagedObject {
   ...
}

或者

@objcMembers public class LPFile: NSManagedObject {
   ...
}

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

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

标签: objective-cswiftcocoacore-datansmanagedobject

解决方案


@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 框架 ( NS, UI, CF, ...) 中的这两个字母前缀和 3rd 方代码中的三个字母前缀的原因。一些第 3 方开发人员仍然使用两个字母,但这是另一回事。

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

例如检查NSEntityDescription类 -managedObjectClassName属性:

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

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


推荐阅读