//转载:http://www.jianshu.com/p/82863c103164
Swift的构造器(Initializer)设计的太复杂,分为指定构造器(Designated Initializers) 和 便利构造器(Convenience Initializers)。指定构造器类似于一般语言的构造器,便利构造器的使用就比较复杂了。
Swift 采用以下三条规则来限制构造器之间的代理调用:
规则 1
指定构造器必须调用其直接父类的的指定构造器。
规则 2
便利构造器必须调用同一类中定义的其它构造器。
规则 3
便利构造器必须最终以调用一个指定构造器结束。
一个更方便记忆的方法是:
• 指定构造器必须总是向上代理
• 便利构造器必须总是横向代理
这些规则可以通过下面图例来说明
构造器的继承规则:
规则 1
如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
规则 2
如果子类提供了所有父类指定构造器的实现,不管是通过规则1继承过来的,还是通过自定义实现的,它将自动继承所有父类的便利构造器。
假设有这样的需求:继承NSWindowController,然后在构造器中做初始化,如下代码
public class PreferencesWindowController:NSWindowController{
var name:String?;
public override init(window: NSWindow?) {
super.init(window: window);
name = "Preferences";
}
}
这样写会报错:
error: 'required' initializer 'init(coder:)' must be provided by subclass of 'NSWindowController'
^
AppKit.NSWindowController:4:34: note: 'required' initializer is declared in superclass here
@objc(initWithCoder:) required init?(coder: NSCoder)
init(coder:)构造器是所谓的必要构造器(Required Initializers)
,当重载了一个指定构造器,就必须重载父类的必要构造器。
修改如下:
public class PreferencesWindowController:NSWindowController{
var name:String?;
public override init(window: NSWindow?) {
super.init(window: window);
name = "Preferences";
}
required public init?(coder: (NSCoder!))
{
super.init(coder: coder)
}
}
这样编译正确了,但如果按下面的方式调用
var pwc = PreferencesWindowController(windowNibName:"MyWindowControllerNib");
还是会报错:
error: cannot invoke 'init' with an argument list of type '(windowNibName: StringLiteralConvertible)'
var pwc = PreferencesWindowController(windowNibName:"MyWindowControllerNib");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
需修改如下:
public class PreferencesWindowController:NSWindowController{
var name:String?;
public override init(window: NSWindow?) {
super.init(window: window);
name = "Preferences";
}
required public init?(coder: (NSCoder!))
{
super.init(coder: coder)
}
public override init()
{
super.init()
}
}
这其实就是要符合构造器的继承规则2:
如果要使用父类的便利构造器,要么不重载父类的指定构造器,要么就必须全部重载父类的指定构造器。
如果只新建一个便利构造器来利用父类的便利构造器,就不必要重载父类的指定构造器,见如下代码:
public class PreferencesWindowController:NSWindowController{
var name:String?;
public convenience init(name:String){
self.init(windowNibName:"MASPreferencesWindow");
self.name = name;
}
}
var pwc = PreferencesWindowController(name:"Preferences");
Swift 1.1要引入了所谓的可失败(failable initializers)
即构造器可能返回nil,用init?表示:
class Product {
let name: String!
init?(name: String ) {
if name.isEmpty { return nil }
self.name = name
}
}
实际使用中的一个案例:
public class PreferencesViewController :NSViewController{
public override convenience init() {
self.init(nibName:"PreferencesView" , bundle: nil );
}
}
会报错:
error: a non-failable initializer cannot delegate to failable initializer 'init(nibName:bundle:)' written with 'init?'
self.init(nibName:"PreferencesView" , bundle: nil );
因为init(nibName: bundle:)是个failable initializers,而init()是non-failable initializer,不可失败构造器。不可失败构造器不可代理可失败构造器。下面是一种解决方法,通过init!来转换failable initializers到non-failable initializer。
public class PreferencesViewController :NSViewController{
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
public override init!(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil?, bundle: nibBundleOrNil?)
}
public override convenience init () {
self.init(nibName:"PreferencesView" , bundle: nil );
}
}
因为Swift的初始化机制和optional的引入,所以设计了如此繁琐的构造器规则,也许可以在parser阶段不检查,通过compiler阶段检查来简化这个机制。