问题
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