Swift init方法的一些修饰符

iOS Feb 6, 2016

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.
You do not have to provide convenience initializers if your class does not require them. Create convenience initializers whenever a shortcut to a common initialization pattern will save time or make initialization of the class clearer in intent.

大意是说,Convenience 关键字不是那么重要的,他的作用是定义一个用于补充 Designated init 方法的方法,使 init 方法可以更方便使用,所以一般情况是用不到的。

用法也很简单,看下面例子

import UIKit

class SomeClass: NSObject {
    
    var locationX: CGFloat
    var locationY: CGFloat
    
    init(x: CGFloat, y: CGFloat) {
        
        locationX = x
        locationY = y
    }

    convenience init(point: CGPoint) {
        
        self.init(x: point.x, y: point.y)
    }
}

class SomeSubclass: SomeClass {
    
}

let position = SomeSubclass(point: CGPointZero)

print(position.locationX)

这里有一个有意思的现象,如果 SomeSubclass 里面有

override init(x: CGFloat, y: CGFloat) {
    super.init(x: x, y: y)
}

或者什么都不写,才可以使用

let position = SomeSubclass(point: CGPointZero)

@onevcat 所撰写的 DESIGNATED,CONVENIENCE 和 REQUIRED 里面写道

子类中也强制 (显式或者隐式地) 调用 super 版本的 designated 初始化,所以无论如何走何种路径,被初始化的对象总是可以完成完整的初始化的。

那么问题来了,designated 初始化是什么,designated 我也翻译不好,designated initializer 就是指自己的 init 方法,为什么一定是自己的 init 方法呢,因为 super 的 init 方法需要加上 override 关键字。

Convenience 是必须调用 designated initializer,如果试图在 convenience 里面写成

locationX = point.x
locationY = point.y

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

借用苹果文档一张图就是这样一个关系

required

如果第一个方法这样改写

required init(x: CGFloat, y: CGFloat) {
    
    locationX = x
    locationY = y
}

SomeSubclass 中如果自己有一个 init() 方法,编译器就会报错,让 insert required init(x: CGFloat, y: CGFloat) 这个方法。

当然了,如果 SomeSubclass 里面没有 init() 方法,designated initializer 依旧是会被隐式调用的。

所以,如果父类中有 convenience 方法想在子类中使用,或者父类的 init() 方法想在子类中也能用,就最好在 init() 加上 required 关键字。

Tags

Jie Li

🚘 On-road / 📉 US Stock / 💻 Full Stack Developer / 🎓 Grad Student / ®️ ENTJ

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.