一些初始化中的关键字

convenience required designated fatalError

Posted by Genie on June 13, 2020
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class ClassA {
    let numA: Int
    
    init(num: Int) {
        numA = num
    }

    convenience init(bigNum: Bool) {
        self.init(num: bigNum ? 100 : 1)
    }
}

class ClassB: ClassA {
    let NumB: Int
    override init(num: Int) {
        NumB = num + 1
        super.init(num: num)
    }
}

let bbb = ClassB(bigNum: true)

bbb.NumB //101
bbb.numA //100

Swift 的 init 只可能被调用一次,因此在 init 中我们可以为常量进行赋值,而不会引起任何线程安全的问题

designated

在 Objective-C 中,init 方法是非常不安全的:没有人能保证 init 只被调用一次,也没有人保证在初始化方法调用以后实例的各个变量都完成初始化,甚至如果在初始化里使用属性进行设置的话,还可能会造成各种问题,虽然 Apple 也明确说明了不应该在 init 中使用属性来访问,但是这并不是编译器强制的,因此还是会有很多开发者犯这样的错误。

所以 Swift 有了超级严格的初始化方法。一方面,Swift 强化了 designated 初始化方法的地位。Swift 中不加修饰的 init 方法都需要在方法中保证所有非 Optional 的实例变量被赋值初始化,而在子类中也强制 (显式或者隐式地) 调用 super 版本的 designated 初始化,所以无论如何走何种路径,被初始化的对象总是可以完成完整的初始化的。

required

加上 required 在方法前,是要求子类必须对其实现

required & convenience 可同时使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class ClassA {
    let numA: Int

    init(num: Int) {
        numA = num
    }

    required convenience init(bigNum: Bool) {
        self.init(num: bigNum ? 100 : 1)
    }
}

class ClassB: ClassA {
    let NumB: Int
    override init(num: Int) {
        NumB = num + 1
        super.init(num: num)
    }

    required convenience init(bigNum: Bool) {
        self.init(num: bigNum ? 101 : 1)
    }
}

let bbb = ClassB(bigNum: true)

bbb.NumB //102
bbb.numA //101

写不写 required 也可以重写 init(bigNum:),最好写上好理解。 当你在 ClassA 写上 ` required convenience init(bigNum: Bool) ` 这里系统不会提示你让你在 ClassB 中实现,但是你可以自己copy 来实现也是没问题的

convenience

init 多态方法,在init 前加上 convenience 补充使用,初始化方法的 ‘二等公民’ 只要override init 方法,convenience 的init 方法在子类中可直接使用

But:

  1. convenience init方法必须调用本类中 的designated初始化方法完成初始化设置。
  2. convenience的初始化方法不能被子类重写或者是从子类中以super的方式被调用

fatalError

1
2
3
required init(coder: NSCoder) {
  fatalError("NSCoding not supported")
}

当子类无需实现,但是又没办法不得不去实现,如上面

如果父类中有个方法无需每个子类都要去实现,但是往往你可能会在子类中用到,这时候你没有注意到,可以通过 fatalError 来强调子类必须实现的方法 , 这样他会在编译的时候打断你。 但是我觉得使用 required会更好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyClass1 {
    func methodMustBeImplementedInSubclass() {
        fatalError("这个方法必须在子类中被重写")
    }
}

class YourClass: MyClass1 {
    override func methodMustBeImplementedInSubclass() {
        print("YourClass 实现了该方法")
    }
}

class TheirClass: MyClass1 {
    func someOtherMethod() {

    }
}

YourClass().methodMustBeImplementedInSubclass()
TheirClass().methodMustBeImplementedInSubclass()

build failed log

1
2
error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.

@discardableResult 忽略返回值,可以不接收

1
2
3
4
5
6
7
@discardableResult func testsss() -> String {
    print("test")
//    return "suncheng testing"
    fatalError("test error")
}

testsss()

通过 fatalError 不再需要指定返回值