Swift: @objc(…) Attribute

荒凉一梦 提交于 2021-02-11 17:10:45

问题


In Apple-generated code (Core Data NSManagedObject subclasses, for example) I see this:

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

My question is: why is the @objc declaration done as above, instead of:

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

or

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

What is special about the separate @objc(identifier) declaration? I can't seem to find documentation about it and googling just turns up the other two approaches. Thanks.

(NB: I'm aware class prefixes are not idiomatic Swift.)


回答1:


@Alladinian is right. Suppose you have a framework SharedSwift with two classes:

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

You can import this framework in the Objective-C code and use these two classes directly:

@import SharedSwift;

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

But because Objective-C has a powerful runtime, you can do a lot of magic. One example is the NSClassFromString function:

- (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);

And the output is:

(null) <Bar: 0x6000015c4200>

What's the problem? className ...

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

... returns SharedSwift.Foo in one case (@objc) and Bar in another one (@objc(Bar)).

SharedSwift.Foo Bar

Let's add AnotherSwift framework to the mix with same classes and try to use Foo from both:

@import SharedSwift;

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

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

Works as expected. Try the same thing with the Bar class:

@import SharedSwift;

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

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

Bar class is defined in both frameworks and which one will be used is undefined. See the error in the console when you try this:

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.

What's the reason for this?

As you can see, there's a difference between Objective-C code (@import SharedSwift & direct usage of Foo) & Objective-C runtime name (NSClassFromString, ...).

There's one namespace for everything in the Objective-C world. This is the reason for these two letters prefixes in the Apple frameworks (NS, UI, CF, ...) and three letters prefixes in the 3rd party code. Some 3rd party developers still do use two letters, but that's another story.

Swift has more namespaces - they're based on modules. It's a safe bet to include module name when the pure @objc attribute is used. To avoid possible ambiguity.

Check the NSEntityDescription class for example - managedObjectClassName property:

The name of the class that represents the receiver’s entity.

There's a lot of stuff around which leverages Objective-C runtime features, lot of stuff is based on just names (strings), ...



来源:https://stackoverflow.com/questions/63024181/swift-objc-attribute

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!