Swift init修饰符convenience 和require

convenience

Convenience initializers are secondary, supporting initializers for a class. You can define a convenience initializer to call a designated initializer from the same class as the convenience initializer with some of the designated initializer’s parameters set to default values. You can also define a convenience initializer to create an instance of that class for a specific use case or input value type.”

Convience init 是类中比较次要的,辅助型的构造器,可以调用便利构造来调用同一个累中的制定的构造器,并且为其提供默认值,从中我么可以知道,便利构造器的使用接口肯定是这样子的

1
convenience init(parameters) {
    self.init(parameters); 
}

在便利构造器中必须调用同一类中定义的其他构造器(制定的或者是便利的,但是最终的便利构造器必须调用制定的构造器)

上图翻译成程序

1
class Person: NSObject {
    var age: Int
    var name: String
    init(age:Int, name:String) {
        self.age = age;
        self.name = name;
    }
    
    convenience init(age:Int, firstName:String, lastName:String) {
        self.init(age:age, name:firstName+lastName);
    }
    
    convenience init(age:Int, firstName:String, lastName:String, height:CGFloat) {
        self.init(age:age, firstName:firstName, lastName:lastName);
    }
}


class Father: Person {
    var address: String
    init(age:Int, name:String, address:String) {
        self.address = address;
        super.init(age: age, name: name);
    }
    
    convenience init(age:Int, firstName:String, lastName:String, address:String) {
        self.init(age:age,name:firstName+lastName, address:address);
    }
}

这些swift中约定俗成的规则,支持影响类定义如何实现,并不会影响类中的结构,下图中展示了四个累的更复杂的层次结构(程序略)

不管这些初始化函数怎么花哨,走何种路径,类的property都会被完整的初始化,如果在上面的程序示例中的convenience init(age:Int, firstName:String, lastName:String)函数中没有调用类指定的初始化(designated initializer), 程序将会报错

use of 'self' in delegating initializer before self.init is called 
self.init isn't called on all paths in delegating initializer

Convience init 应用比较多的场景还是extension

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
extension UIColor {
convenience init(hex: Int, alpha: CGFloat) {
let r = CGFloat((hex & 0xFF0000) >> 16) / 255.0
let g = CGFloat((hex & 0x00FF00) >> 8) / 255.0
let b = CGFloat(hex & 0x0000FF) / 255.0
self.init(red: r, green: g, blue: b, alpha: alpha)
}
convenience init(hexString str: String, alpha: CGFloat) {
let range = NSMakeRange(0, str.characters.count)
let hex = (str as NSString).replacingOccurrences(of: "[^0-9a-fA-F]", with: "", options: .regularExpression, range: range)
var color: UInt32 = 0
Scanner(string: hex).scanHexInt32(&color)
self.init(hex: Int(color), alpha: alpha)
}
}

required

“Write the required modifier before the definition of a class initializer to indicate that every subclass of the class must implement that initializer:”

在类的构造器前添加 required 修饰符表明所有该类的子类都必须实现该构造器

swift是吸取各种语言的精华语法,让swift的开发效率在Objective-C的基础上提高了好几个档次,而且swift也开启了server 的开发。通常情况下,我们说到一个类要继承一个父类的方法,往往会像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class SomeClass {
var str:String
init(str:String) {
self.str = str
}
}

class SomeSubclass: SomeClass {
override init(str:String) {
super.init(str:str)
}
init(i:Int) {
super.init(str:String(i))
}
}

var SomeSubclass(str:"Hello Swift")

那么我们首先要在init()方法前加上override修饰符,表示MySubClass重写了其父类的init()方法,然后还要调用父类的init()方法,并将参数一并传给父类的方法。当子类的初始化方法参数类型与父类的初始化方法参数类型不同时,我们就不必在子类的初始化方法前加override修饰符了,但是要把子类初始化方法的参数类型转换为符合父类初始化方法的参数类型,然后传给父类的初始化方法。这个和java中的写法是一样的,但是如果在父类的init之前添加上 required 呢。

根据规则代码结构呈现应该是这样的

1
class SomeClass {
    required init() {
// 构造器的实现代码 }
}
class SomeSubclass: SomeClass {
    required init() {
// 构造器的实现代码 }
}

如果SomeSubClass 中没有实现父类中的require init方法会报下面的错误
‘required’ initializer ‘init(params)’ must be provided by subclass of ‘SomeClass’

所以完整的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class SomeClass {
var str:String
required init(str:String) {
self.str = str
}
}

class SomeSubclass: SomeClass {
required init(str:String) {
super.init(str:str)
}
init(i:Int) {
super.init(str:String(i))
}
}

var SomeSubclass(str:"Hello Swift")

当然要是SomeSubclass没有任何初始化函数也是不会报错的,他用的是父类的规则,这个条件之成立于子类中没有任何的初始化函数,比如下面的情况也是不行的

编译器会告诉你
required init(str:) has not been implemented