$ ssh-keygen -t rsa -C "qiweipeng@hotmail.com" Generating public/private rsa key pair. Enter file in which to save the key (/Users/qiweipeng/.ssh/id_rsa):
Enter file in which to save the key (/Users/qiweipeng/.ssh/id_rsa):/Users/qiweipeng/.ssh/id_rsa_github Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in id_rsa_github. Your public key has been saved in id_rsa_github.pub. The key fingerprint is: SHA256:SRqk4YpV6A2tWc1JZDLn5jJVycjYSpffl+0Y55v+meM qiweipeng@hotmail.com The key's randomart image is: +---[RSA 2048]----+ | o=O*=.. | | oo=XX.o | | ..Boo=... o | | o+.o+ +... + o | |. . o o S . * | | o . o | | o | | o.o| | .oEo| +----[SHA256]-----+
$ ssh -T git@github.com The authenticity of host 'github.com (13.229.188.59)' can't be established. RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'github.com,13.229.188.59' (RSA) to the list of known hosts. Hi qiweipeng! You've successfully authenticated, but GitHub does not provide shell access.
“Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 4.1).” iBooks.
这是官方的定义,从中可以看到:
构造器可以使用的场景不仅仅是类,还可以是结构体和枚举(我们知道 Swift 中的结构体和枚举的强大已经不是 Objective-C 可以相比的了);
实例在构造完成之前是无法使用的;
构造过程主要是包括存储型属性的赋初值的过程,另外还包括一些其他必须的设置和初始化工作;
为了阅读的通畅,我们先展示类、结构体、枚举的构造过程,这样可以有一个初步的认识。之后再进一步深入。
类的构造过程
1 2 3 4 5 6 7 8 9 10 11
classPerson { var name: String let id: String init(name: String, id: String) { self.name = name self.id = id } }
let roger =Person(name: "Roger", id: "123456")
这里定义一个 Person 类,拥有两个属性,name(因为名字可以更改,所以是 var,又因为所有人都要有名字,所以是非可选型)和 id(ID 一般是无法更改的,所以是 let)。Swift 中的构造器没有返回值,在其中我们完成了两个属性的初始化。
enumRating { case perfect case great case good case bad init?(score: Int) { switch score { case90...100: self= .perfect case80..<90: self= .great case60..<80: self= .good case0..<60: self= .bad default: returnnil } } }
let rating1 =Rating.perfect let rating2 =Rating(score: 98)
通常情况下,枚举变量可以通过 类型名.case名 的形式构造,这也是绝大多数的使用方式,不过 Swift 中我们仍然可以为枚举添加构造器。上面的例子中,我们通过得分来构造一个表示等级的枚举变量。最终两种构造方式都可以构造出一个枚举变量/常量。
classPerson { var name: String var id: String="000000" init(name: String) { self.name = name } }
classStudent: Person { var studentNumber: String init(name: String, id: String, studentNumber: String) { // 第一段构造,先进行本类的属性的初始化,再通过调用父类构造器完成继承自父类的属性的初始化 self.studentNumber = studentNumber super.init(name: name) // 第二段构造,为父类的其他提供默认值的属性初始化;完成其他逻辑,此时已经可以使用 self 调用实例方法或属性 self.id = id } }
let harry =Student(name: "Harry", id: "654321", studentNumber: "123456")
这个例子中,子类的构造器里我们做了三件事:
为本类的属性初始化
通过调用父类构造器完成从父类继承的属性的初始化
为父类中已经提供默认值的属性初始化(当然这一步还可以做更多其他逻辑,可以调用实例方法)
“Notice that the initializer for the EquilateralTriangle class has three different steps:
Setting the value of properties that the subclass declares.
Calling the superclass’s initializer.
Changing the value of properties defined by the superclass. Any additional setup work that uses methods, getters, or setters can also be done at this point.”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 4.1).” iBooks.
那么为什么还要第二段构造呢?我们可以看上面的例子,这个例子中,父类继承的属性 id 如何在子类中初始化呢?首先我们要知道父类继承来的属性必须通过调用父类构造器完成初始化,可是此时父类提供的构造器是把 id 属性按照其默认值进行初始化的,也就是说,在子类中,我们最初构造出的这个实例,其 id 属性只能是它的默认值 000000,那么我们在子类还希望按照自己的意愿为其赋初值啊?那么就应该在第二段构造过程中进行,这一段构造过程本质上其实并不再是赋初值的过程,而是更改值的过程(之前写短文讨论过属性观察器的触发时机,第二段构造中属性的赋值已经可以触发属性观察器了)。
额外说明一个小地方,这里 super 调用父类构造器是必须进行的吗?答案是是的!但还是有可以省略的情况的(省略不写不代表没有调用),但这既少见也不建议,比如下面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classPerson { var name: String="Guest" }
classStudent: Person { var studentNumber: String init(studentNumber: String) { self.studentNumber = studentNumber } }
let harry =Student(studentNumber: "123456") harry.name // "Guest"
Swift 中类的构造过程与 Objective-C 的对比
很多程序员从 Objective-C 转到 Swift 后,都会有一个疑惑。
为什么 Objective-C 中 都是先调用 super,再为属性赋值,而 Swift 中却是颠倒,必须先为属性赋值,才能调用 super 呢?
通常我们都是这样写的,很多文章里的解释是,我们先调用 super 完成父类的初始化,然后再进行本类的初始化。
我们对比一下 Swift,发现什么了么?其实很简单,这里所谓的“父类的初始化”,就是 Swift 中的第一段的构造过程,只不过 Objective-C 中所有属性都有默认值,那就是 0(nil 也是 0)。所以第一阶段构造实例的过程完全是把所有属性按照默认值去初始化构造出了这个实例,而所谓的“本类的初始化”就很像 Swift 中的第二段构造过程,这里实例已经创建(如果 if 判断成立的话),之后在里面其实是修改属性的值而已,从 0 修改成一个非 0 的值。
也就是说 Swift 的构造过程相对更灵活,它可以不必一开始只能用默认值去构造这个实例。所以 Swift 的构造过程并不是说把 super 交换到了后面,而是在 super 前面增加了更多的灵活性。
“Swift’s two-phase initialization process is similar to initialization in Objective-C. The main difference is that during phase 1, Objective-C assigns zero or null values (such as 0 or nil) to every property. Swift’s initialization flow is more flexible in that it lets you set custom initial values, and can cope with types for which 0 or nil is not a valid default value.”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 4.1).” iBooks.
“Initializers can call other initializers to perform part of an instance’s initialization. This process, known as initializer delegation, avoids duplicating code across multiple initializers.”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 4.1).” iBooks.
再往前面的例子中,我们有继承的例子,那些例子中我们在子类的构造器中通过 super 关键字调用了父类的构造器,事实上这也是构造器代理。
那么在类中,构造器代理满足什么样的规则呢?
Rule 1 A designated initializer must call a designated initializer from its immediate superclass. Rule 2 A convenience initializer must call another initializer from the same class. Rule 3 A convenience initializer must ultimately call a designated initializer.”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 4.1).” iBooks.
总结下来就是三点:
指定构造器只能调用父类的指定构造器(注意无法调用父类的便利构造器哦)
便利构造器只能调用本类中的其他构造器(便利构造器中无法使用 super 关键字而只能使用 self)
let harry =Student(name: "Harry", age: 11, studentNumber: "123456") let hermione =Student(name: "Hermione", age: 12) let ron =Student(firstName: "Ron", lastName: "Weasley", age: 11)
Assuming that you provide default values for any new properties you introduce in a subclass, the following two rules apply: Rule 1 If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers. Rule 2 If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 4.1).” iBooks.
classStudent: Person { var studentNumber: Int init(studentNumber: Int, name: String) { self.studentNumber = studentNumber super.init() self.name = name } }
let andy =Student(studentNumber: 123456, name: "Andy") // 构造函数触发属性观察器
Setting the value of properties that the subclass declares.
Calling the superclass’s initializer.
Changing the value of properties defined by the superclass. Any additional setup work that uses methods, getters, or setters can also be done at this point.”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 4.1).” iBooks.