更具体地说,即使继承了所有其他方便的init,imageNamed也不会暴露给NSImage的Swift子类。
根据苹果的文档Objective-C工厂方法"在Swift中被映射为方便的初始化程序。"
因此,NSImage工厂方法
+ (NSImage *)imageNamed:(NSString *)name
被映射到Swift的
NSImage(named: "anything")
此外,在Swift编程语言的书中,我们看到子类初始值设定项继承的规则是:
"规则1如果您的子类没有定义任何指定的初始化器,它会自动继承所有超类指定的初始化程序。
规则2如果您的子类提供了其所有超类指定初始化器的实现——要么按照规则1继承它们,要么在其定义中提供自定义实现——那么它会自动继承所有超类便利初始化器。"
因此,子类
class T : NSImage {
}
将继承所有超类的初始值设定项,包括指定的和方便的,但以下内容将不起作用
let I = T(named: "anything")
我错过了什么?
named:
初始值设定项的返回类型为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 */
而其他(并非所有)初始化程序没有返回类型
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 */
因此,如果您有一个T
类,它是NSImage
的子类,您会期望T(named:)
返回T
的实例,但如果您不提供自己的实现,则会调用超类实现,正如我们在声明中看到的,它返回NSImage
。
为什么它与所有其他初始化程序不同?我不知道
也许NSImage
使用的底层实现和缓存机制导致了这种不一致性。但更重要的是,+[NSImage imageNamed:]
不调用任何指定的NSImage
初始化器。
看看我用Cocoa Touch的UIImage
制作的这个例子:https://gist.github.com/bartekchlebek/d61154add8525218ae3a
您可以在那里看到,我创建了一个名为MyImage
的UIImage
子类,并覆盖了所有UIImage
的初始值设定项,将NSLog
放入其中并返回nil。
在中
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
我调用-[MyImage imageNamed:]
并注意到,放置在指定初始化器中的NSLog
都没有打印出来。我还打印了用-[MyImage imageNamed:]
创建的实例的类,您可以看到它不是MyImage
,而是UIImage
(抛开我在所有初始化程序中返回nil
的事实不谈;)
总之,+[UIImage imageNamed:]
不调用UIImage
的任何指定初始化程序,因此重写它们不会使+[MyImage imageNamed:]
返回MyImage
的实例。我希望NSImage
也能有同样的表现。
这种微妙之处导致Swift
不继承named:
初始值设定项,因为swift需要便利初始化程序通过指定的初始值设定器,而Objective-C不需要。