文章目录
  1. 1. 1、Swift初始化过程概要
  2. 2. 2、定义构造器的几种做法
  3. 3. 2.1、默认构造器
  4. 4. 2.2、自定义构造器
  5. 5. 3、值类型的构造器代理

因为Swift中涉及到的初始化的知识点比较多,所以学习笔记分2部分进行。第一部分学习基础的初始化知识,第二部分学习类中对初始化继承的知识。

1、Swift初始化过程概要

初始化过程对于一些面向对象的语言,如C++,Objective-C来说,是一个很重要的概念,不过使用和理解起来,会稍微有点难度,因为初始化过程,对于类的设计者来说,第一要区分默认构造器和自定义构造器的一些规则,第二是如果存在类继承的情况下,如何对父类进行初始化操作,也有一些规则。

对于Swift来讲,以上说的这些规则,在Swift官方文档当中,就花了很多篇幅来介绍初始化过程,尤其是类继承的初始化的一些规则。

这2部分学习笔记还是根据官方文档的顺序来展开,第一部分主要讲下如何对结构体,枚举和类进行初始化,Swift究竟提供了几种做法进行初始化过程,还有值类型(结构体,枚举)和类初始化的一些区别,另外由于值类型不能继承,所以一个值类型需要包含另外一个值类型时,进行的初始化操作就比较简单。这被称作值类型的构造器代理。而类继承下的初始化操作,我们放在第二部分进行介绍。

好了,说了那么多废话,还是进入正题。我们首先来看看,Swift的初始化有那些规则,总结如下,一些细节的学习,会在第二部分展开:

  • 初始化过程其实就是对存储属性进行赋值,Swift不允许在初始化过程结束后,还有存储属性存在不确定的值
  • Swift中使用init()作为构造器,且该定义不允许有返回值
  • 值类型和引用类型在一定条件都有默认的构造器,但是默认的构造器的定义会有不同
  • 也可以自定义构造器,但是必须使用init作为函数名,理论上可以定义很多构造器,只要语意上不冲突即可(参数定义不同)

2、定义构造器的几种做法

构造器要做的工作,其实就是对存储属性进行赋值。我们其实可以想到,存储属性在定义时候也可以进行赋值。如果对所有定义的存储属性定义时候已经进行了赋值,那就可以不提供构造器。编译器自动会生成一个默认的构造器。

2.1、默认构造器

Swift中会提供默认构造器需要满足以下条件:

  • 存储属性都有默认值(定义时赋值)
  • 没有实现其他构造函数
  • 类需要是基类

类和结构体提供的默认构造器有一些区别,结构体会提供一个逐一成员默认构造器。我们首先看看类的默认构造器的例子:

1
2
3
4
5
6
7
8
9
class ShoppingListItem
{
var name: String?
var quantity = 1
var purchased = false
// No init(...) initializer
}
let item = ShoppingListItem()

可以看到,存储属性quantitypurchased都赋值了默认值,而nameOptional的,编译器默认会赋值为nil。所以这里满足默认构造器的第一个条件。而类中也没实现其他构造器,类也是基类。所以编译器就会自动提供一个init()的构造器。

如果我们提供一个构造器会如何呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ShoppingListItem
{
var name: String?
var quantity = 1
var purchased = false
init(name: String)
{
self.name = name
}
}
//Error: Missing argument for parameter 'name' in call
let item = ShoppingListItem()

上例中,提供了一个对name进行赋值的构造器,这时候,调用默认构造器就会报错。这说明,如果自己定义了一个或者多个构造器,编译器就不会生成默认构造器了。

接下来让我们看看结构体的默认构造器的情况。还是先来看看代码吧:

1
2
3
4
5
6
7
8
9
10
struct Size
{
var width = 0.0
var height = 0.0
var description: String?
//init(){}
}
let twoByTwo = Size(width: 2.0, height: 2.0, description: "")
let twoByTwo1 = Size()

简单点说,对于结构体的默认构造器,除了跟类一样的一个无参的构造器以外。还提供了按照存储属性作为参数的逐一成员构造器。这个构造器可以对存储属性赋值。

另外一点需要注意的是,即使是结构体的存储属性没有初始值,编译器也会给结构体提供逐一成员构造器,而这个情况下,就没有了默认的无参数构造器。以下是一个例子:

1
2
3
4
5
6
7
8
9
10
11
struct Size
{
var width: Double
var height: Double
var description: String?
}
let twoByTwo = Size(width: 2.0, height: 2.0, description: "")
//Error:Missing argument for paramenter 'width' in call
let twoByTwo1 = Size()

在上面的例子中,存储属性width和height都没有默认值,所以使用无参的构造器会产生编译错误。而逐一成员构造器可以继续使用。

2.2、自定义构造器

先出一些要点,然后再举例:

  • 自定义构造器以后,编译器就不会产生默认的构造器了
  • 构造器可以重载
  • 自定义构造器内部参数(包括第一个,这跟类中的方法默认行为要区分开)默认具有相同的外部参数名,也可以修改这种默认行为
  • 构造器内可以对常量进行一次赋值,前提是定义时,常量没有赋值
  • Optional变量默认已经赋值为nil,定义或者构造器都可以忽略它。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Celsius
{
var temperatureInCelsius: Double = 0.0
// Initialize our temperature from Fahrenheit
init(fromFahrenheit fahrenheit: Double)
{
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
// Initialize our temperature from Kelvin
init(kelvin: Double)
{
temperatureInCelsius = kelvin - 273.15
}
}
let boilingPotOfWater = Celsius(fromFahrenheit: 212.0)
let freezingPointOfWater = Celsius(kelvin: 273.15)
//Error:Cannot find an initializer for type 'Celsius'
//that accepts an argument list of type '(Double)'
let freezingPointOfWater1 = Celsius(273.15)

定义一个摄氏度的结构体,摄氏度的温度可以从华氏温度和开尔文温度计算而来。
第一个构造器提供了显式的外部参数名,第二个构造器没有提供外部参数名,但调用时,编译器会要求加上外部参数名,否则会产生编译错误。

我们也可以使用”_”来改变默认行为,让构造器不用输入外部参数名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct Color
{
var red = 0.0, green = 0.0, blue = 0.0
init(red: Double, green: Double, blue: Double)
{
self.red = red
self.green = green
self.blue = blue
}
init(_ red: Double, _ blue: Double)
{
self.red = red
self.green = 0
self.blue = blue
}
}
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let purple = Color(1.0, 0.5)

下面,我们举例说明Optional,常量在构造函数中的表现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class SurveyQuestion
{
let text: String
var response: String?
init(text: String)
{
// We only need to initialize 'text'
self.text = text
}
func ask() -> String
{
return text
}
}

Optional可以在初始化时不进行任何赋值操作,默认值为nil。而常量如果在定义时没有进行赋值,必须在构造器进行赋值,否则会产生编译器错误,而且赋值了一次以后,常量值就不能进行改变了。

3、值类型的构造器代理

构造器代理,简单点说,就是一个构造器可以调用另外一个构造器来完成构造过程。值类型(结构体)因为没有继承,所以构造器代理就是不同重载版本的构造器之间的调用。而类由于有继承,构造器代理就涉及到父类和子类等继承关系,在第二部分学习中,会专门介绍类的继承如何来进行初始化。
举个例子来说明结构体的构造器代理

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
29
30
31
32
33
34
35
36
37
38
struct Size
{
var width: Double = 0
var height: Double = 0
}
struct Point
{
var x = 0.0
var y = 0.0
}
struct Rect
{
var origin = Point()
var size = Size()
init() {}
init(origin: Point, size: Size)
{
self.origin = origin
self.size = size
}
init(center: Point, size: Size)
{
let originX = center.x - size.width / 2
let originY = center.y - size.height / 2
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
let basicRect = Rect()
let originRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))

Rect定义了3个构造器,前2个比较正常,定义与默认无参构造器和逐一成员构造器一样,第三个构造器通过center来计算origin的位置,最后调用第二个构造器来完成初始化。
这里,第三个构造器调用第二个构造器完成初始化,就叫做构造器代理。

参考资料:
Swift官方文档

文章目录
  1. 1. 1、Swift初始化过程概要
  2. 2. 2、定义构造器的几种做法
  3. 3. 2.1、默认构造器
  4. 4. 2.2、自定义构造器
  5. 5. 3、值类型的构造器代理