SwiftNote-10
2016年05月16日 Swift

构造方法

与其他编程语言类似,Swift的构造方法也是在实例初始化的时候为其存储属性赋初值的操作。使用init关键字,可以自定义参数,但是不指定返回值。形式如下:

1
2
3
4
init([parameterlist])
{
// initialize code
}

存储属性赋初值

  1. 在存储属性没有默认值时候,必须在构造方法完成的时候,所有的存储属性都有对应初值(可选类型默认是nil)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Constructor
    {
    var str:String
    var optionalVal:Int?
    init()
    {
    // 为str赋初值
    self.str = "Constructor init method has been called"
    print(self.str)
    // optionalVal 默认是nil值
    }
    }
  2. 定义时默认赋初值

    1
    2
    3
    4
    5
    6
    class TestDefaultAssign
    {
    var defaultVal = 10
    }
    let tda = TestDefaultAssign()
    print(tda.defaultVal) // 不需要构造方法赋初值

自定义构造方法

  1. 自定义构造参数

    像一般方法一样,init方法也可以传入参数,通过实例化的时候传入的参数来初始化对应的属性,如上例可自定义构造参数,修改如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Constructor
    {
    var str:String
    var optionalVal:Int?
    // 自定义二个参数
    init(str:String, optionInt:Int)
    {
    self.str = str
    self.optionalVal = optionInt
    }
    }
    // 构造实例时传入初始化值
    let con = Constructor(str:"constructor", optionInt:12)
  2. 外部/局部参数名

    因为构造方法都叫init,所以通过参数名来分辨是哪个init方法特别重要,Swift默认给每个没有外部参数名的init方法都生成了一个和局部参数名相同的参数,如果确实想忽略掉对应的外部参数名,还是由下划线显式忽略外部参数名

    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 ConstructorName
    {
    var test:String
    var num:Int = 12
    var isName:Bool

    // 显示忽略外部参数,不需要传入外部参数名
    init(_ test:String, _ num:Int)
    {
    self.test = test
    self.num = num
    self.isName = false
    // init 方法结束必须所有的存储属性都赋初值
    }
    // 显示指定外部参数名,生成实例的时候必须显式指定
    init(externalTest test:String, externalNum num:Int, externalIsName isName:Bool)
    {
    self.test = test
    self.num = num
    self.isName = isName
    }
    }
    let con = ConstructorName("name", 12)
    let con1 = ConstructorName(externalTest: "another", externalNum:12, externalIsName:true)
  3. 可选值与常量赋初值

    某个时刻需要将某个变量赋值为nil,这个变量的类型就需要是可选类型,可选类型默认是初始化为nil的,也就是在定义时就默认有一个nil值。

    1
    2
    3
    4
    5
    class OptionalTest
    {
    var option:Int?
    // 可以用init来进行实例时赋值,这里option就是nil
    }

    常量在定义时可以没有初值,只要保证在实例化完成的时候(也就是init方法结束时)保证常量属性有一个确定的值就可以

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class ConstTest
    {
    let constVal:Int // 可以没有初值
    init(_ constVal:Int)
    {
    self.constVal = constVal // 可以给常量赋值
    }
    }
    let ct = ConstTest(23)
    print(ct.constVal)

默认构造方法

如果结构体或者类里面的所有属性都有对应的初始值,Swift就会自动生成一个把这些属性都设置为初始值的构造方法。

  • 类和结构体

    1
    2
    3
    4
    5
    6
    7
    8
    class Test
    {
    var v1:Int = 10
    var v2:String = "v2"
    // 生成默认构造方法,初值都已存在
    }
    var t = Test()
    print("t.v1 = \(t.v1) t.v2 = \(t.v2)")
  • 结构体的默认带参数逐一构造方法

    即结构体会提供一个默认的逐一构造方法,参数名称就是结构体成员变量的名字,前提是没有给结构体提供自定义的构造方法

    1
    2
    3
    4
    5
    6
    struct Test
    {
    var vs:String = ""
    var vi:Int = 0
    }
    let t = Test(vs:"vs", vi:12) // 默认提供的逐一成员构造方法

构造方法代理

就是类或者结构体内定义多个init方法,通过init之间的调用来减少代码重复。

  • 值类型的构造方法代理,因为结构体或者枚举不能继承,所以构造方法代理简单,直接在内部定义不同的init方法,之间互相调用即可。官方文档的例子特别好

    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
    struct Size
    {
    var wid = 0.0, hgt = 0.0
    }
    struct Point
    {
    var x = 0.0, y = 0.0
    }
    struct Rect
    {
    var origion = Point()
    var size = Size()
    init(){} // 相当于提供默认构造方法,使用Size和Point的默认值来构造实例
    init(origion:Point, size:Size) // 直接指定点和尺寸的Rect构造方法
    {
    self.origion = origion
    self.size = size
    }
    // 根据中心点来和尺寸来确定Rect实例的构造方法
    init(center:Point, size:Size)
    {
    // 由中心点算出原始点,再由上一个构造方法直接构造实例,不再重复代码
    let origionX = center.x - (size.wid / 2)
    let origionY = center.y - (size.hgt / 2)
    // 这里使用Point结构体的逐一成员初始化构造方法
    self.init(origion:Point(x: origionX, y: origionY), size:size)
    }
    }
  • 类的继承&构造方法

    Swift规定类中的存储属性必须在构造方法中完成初值设置,Swift提供了二种构造方法来给类的存储属性赋初值– 指定构造方法便捷构造方法

    1. 指定构造方法

      指定构造方法就是一个普通的构造方法,语法也是init(){}的形式,同时指定构造方法将初始化类中所有的存储属性,并顺着继承链依次向上初始化基类的所有属性,实现基类的实例化。需要注意:每个类都至少有一个指定构造方法

    2. 便捷构造方法

      便捷构造方法是辅助类型的构造方法,不是类里面必须的。便捷构造方法的语法与普通的不同,在init关键字前需要加一个convenience关键字,指明其是便捷构造方法。类似 convenience init(){}这种形式。

    3. Swift明确规定了指定构造方法和便捷构造方法之间的如何调用

      • 指定构造方法必须直接调用其基类的指定构造方法。
      • 便捷构造方法只能只能调用本类中的便捷构造方法。
      • 便捷构造方法必须以调用本类中的指定构造方法来结束。
    4. “二段式”的构造。首先通过类的构造方法来确定属性的初始值。然后在每个类使用前再一次通过实例访问属性来自定义属性的值。

    5. 构造方法的继承与重写

      Swift中的子类不会默认继承基类的的构造方法,因为可能子类已经定义了更详细的构造方法。而简单的继承过来可能会导致生成出的实例不是子类的更详细的构造方法生成出来的。如果子类需要实现与基类相同的参数的构造方法,需要使用override关键字。子类不能使用继承过来的常量属性,因为已在基类中完成了初始化,不能再次更改。二个构造方法的继承规则:

      1. 规则如果子类没有任何自定义的构造方法,那么就会继承基类所有的指定构造方法。
      2. 如果子类有了基类所有指定构造方法的实现(可以像1说的那样完全继承过来的),那么子类也会自动继承所有的基类便捷构造方法。
    6. 可失败的构造方法,构造方法可能返回nil,也就是实例化不成功的情况。使用这种语法形式 init?(){}的形式,生成实例的时候需要使用值绑定的方式来判定实例是否是nil。就相当于返回的是一个可选值。

    7. 子类可以重写基类的可失败构造方法,甚至重写成为一个不可失败的构造方法。

    8. 基类定义需要子类必须实现的构造方法使用required关键字。重写时必须也带着required关键字,此时不再需要override关键字。