Reputation: 1112
More specifically imageNamed does not get exposed to Swift subclasses of NSImage even when all other convenience inits are inherited.
According to Apple's docs Objective-C factory methods "get mapped as convenience initializers in Swift."
Consequently the NSImage factory method
+ (NSImage *)imageNamed:(NSString *)name
gets mapped to Swift's
NSImage(named: "anything")
Also, in the Swift Programming Language book we see that the rules for subclass initializer inheritance are:
“Rule 1 If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
Rule 2 If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.”
It thus follows that the subclass
class T : NSImage {
}
would inherit all the superclass' initializers, both designated and convenience, yet the following will not work
let I = T(named: "anything")
What am I missing?
Upvotes: 4
Views: 565
Reputation: 1664
The reason for that seems to be that the named:
initializer has a return type of NSImage
class NSImage : NSObject, NSCopying, NSCoding, NSSecureCoding, NSPasteboardReading, NSObjectProtocol, NSPasteboardWriting {
/*All instance variables are private*/
init?(named name: String) -> NSImage /* If this finds & creates the image, only name is saved when archived */
while other (not all) initializers have no return type
init(size aSize: NSSize)
init?(data: NSData) /* When archived, saves contents */
init?(contentsOfFile fileName: String) /* When archived, saves contents */
init?(contentsOfURL url: NSURL) /* When archived, saves contents */
init?(byReferencingFile fileName: String) /* When archived, saves fileName */
init(byReferencingURL url: NSURL) /* When archived, saves url, supports progressive loading */
Now because of that, if you have a class T
that is a subclass of NSImage
you would expect T(named:)
to return an instance of T
but if you do not provide your own implementation, the superclass implementation would be called and as we can see in the declaration, it returns an NSImage
.
Why is it different than all other initializers? I don't know.
Perhaps the underlying implementation and caching mechanisms that NSImage
uses lead to this inconsistency. But what's more important is that the +[NSImage imageNamed:]
does not call any of the NSImage
designated initializers.
Have a look at this example I cooked up witg Cocoa Touch's UIImage
: https://gist.github.com/bartekchlebek/d61154add8525218ae3a
You can see there, that I create a subclass of UIImage
called MyImage
and I override all UIImage
's initializers, place NSLog
s in them and return nil.
In
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
I call -[MyImage imageNamed:]
and notice, that none of the NSLog
's placed in the designated initializers are printed. I also print the class of the instance created with -[MyImage imageNamed:]
and you can see that it is not MyImage
but UIImage
(putting aside the fact that I return nil
in all initializers ;) )
In conclusion, +[UIImage imageNamed:]
does not call any of the UIImage
's designated initializers, thus overriding them will not make +[MyImage imageNamed:]
return an instance of MyImage
. And I expect NSImage
to behave the same way.
This subtlety causes Swift
not to inherit the named:
initializer, because swift requires convenience initializers to go through a designated initializer, while Objective-C did not require that.
Upvotes: 3