免费开源的iOS开发学习平台

Swift:15 初始化

初始化是为了使用某个类、结构体和枚举的实例的准备过程。该过程包含为实例中的每个存储属性设置初始值,以及在新实例被使用之前的其他设置和初始工作。通过定义构造器来实现初始化过程,构造器是新建特定类型新实例的特殊方法。

设置存储属性的初始值

在创建实例时,类和结构体必须为所有存储属性设置合适的初始值。存储属性不能处于未设置值的状态。

调用构造器可以创建一个特定类型的新实例。最简单的构造器和不带形参的实例方法一样,使用init关键词。如下实例所示。

struct Point{
    var x:Int,y:Int
    init() {
        x = 0
        y = 0
    }
}

正如上面的示例,可以在构造器中为存储属性设置初始值。此外,还可以在声明属性时为其设置默认属性值。设置默认属性值的方式是在属性定义是为其赋初始值。

struct Point{
    var x:Int=0,y:Int=0
}

自定义初始化

构造器init方法与实例方法一样,也可以在构造器中设置外部使用的形参名和实参名,如果外部形参名省略,默认实参名与形参名同名。当然,也可以在参数名前加下划线(_)来表示方法调用时省略形参名。一个类中可以有多个构造器,构造器之间是通过方法参数个数和类型进行区分的。如下示例演示构造器参数的使用。

class Color{
    var red:Int,green:Int,blue=0
    init(_ red:Int) {
        self.red = red
        self.green = 0
    }
    init(red:Int,withGreen green:Int) {
        self.red = red
        self.green = green
    }
    init(red:Int,green:Int,blue:Int) {
        self.red = red
        self.green = green
        self.blue = blue
    }
    func toString() {
        print("(r:\(self.red),g:\(self.green),b:\(self.blue))")
    }
}

let color1 = Color(10)
color1.toString() //打印结果:(r:10,g:0,b:0)
let color2 = Color(red:10,withGreen:20)
color2.toString() //打印结果:(r:10,g:20,b:0)
let color3 = Color(red: 10, green: 20, blue: 30)
color3.toString() //打印结果:(r:10,g:20,b:30)

如果自定义的类型包含一个逻辑上允许可以为空值的存储属性。不管是因为它无法在初始化时设置它的值,还是因为它可以在之后可以设置为空,都需要将这种属性声明为可选(optional)类型。可选类型的属性将自动初始化为nil,表示特意让这个属性在初始化过程中没有值。

class Robot{
    var question:String
    var anster:String?
    init(question:String) {
        self.question = question
    }
    func think() {
        if question == "今天多少号"{
            anster = "2017-2-8"
        }else{
            anster = "无法明白你的意思"
        }
    }
}

let robot = Robot(question: "今天多少号")
robot.think()
print("\(robot.anster)")

代码执行结果截图如下所示。

默认构造器

当类或者结构体中所有的属性都有默认值,并且没有类中没有自定义构造器时,Swift将为他们提供一个默认构造器。如下示例。

class Color{
    var red:Int=255,green:Int=255,blue=0
}
let color = Color()

如果结构体对所有存储属性提供了默认值并且没有提供自定义的构造器,该结构体将会自动获得一个成员构造器。如下示例所示。

struct Point{
    var x:Int,y:Int
}
let point = Point(x: 10, y: 20)

值类型的构造器代理

构造器可以通过调用其它构造器来完成实例的部分初始化工作。这一过程被称作构造器代理。

对于值类型,使用self.init来引用相同值类型中的其它构造器来打造自定义的构造器。你只能在构造器内部调用self.init。如果值类型自定义了构造器,该类型的默认构造器就无法再被访问。值类型构造器代理使用示例如下。

struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, 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)
    }
    func toString()  {
        print("[origin:(\(origin.x),\(origin.y)),size:(\(size.width),\(size.height))]")
    }
}
let r1 = Rect()
r1.toString()

let r2 = Rect(origin: Point(x:10,y:10), size: Size(width: 6.0, height: 6.0))
r2.toString()

let r3 = Rect(center: Point(x:13,y:13), size: Size(width: 6.0, height: 6.0))
r3.toString()

代码执行结果如下图。

类的继承和初始化

所有类的存储属性,包括所有从超类继承的属性,都必须在初始化的过程中赋初始值。Swift定义了两种构造器来保证所有存储属性获得初始值。分别为指定构造器(designated initializer)和便捷构造器(convenience initializers)。

指定构造器是类的最主要的构造器。指定构造器将类引入的所有属性初始化,并且可以沿着超类链来调用超类的构造函数。每个类都必须至少有一个指定构造器,一般就只有一个。

便捷构造器起到的是辅助的作用。你可以使用便捷构造器调用同类的指定构造器(其中指定构造器的形参被设置为默认值)。还可以针对特定的用途和输入值类型来定义便捷构造器完成生成新实例的工作。在不需要的情况下没必要提供便捷构造器。便捷构造器是一种帮助开发者节省时间和让初始化更加清晰的快速途径。

为了简化指定构造器和便捷构造器的关系。Swift运用了如下三条规则来实现构造器之间的代理调用:

  • 指定构造器必须调用直接超类(如果有超类)的指定构造器。
  • 便捷构造器只能调用同一个类中的其他构造器。
  • 便捷构造器必须以调用指定构造器作为结束。

可以简单的理解为:指定构造器必须向上代理,便捷构造器必须横向代理。

这些规则也可以通过下图说明。

和Objective-C的子类不同,Swift的子类默认不继承超类的构造器。这种行为防止了特殊的子类自动继承简单父类的构造器,并且用这个构造器来创建一个没有完全或正确初始化的子类的新实例。

但是在某些条件满足的情况下,超类构造器是会被自动继承的。在实践中,这意味着你在绝大多数场景中不需要进行构造器覆盖,在任何安全的时候,用最小的代价来继承超类构造器。

假设你为所有子类引入的新属性都提供了默认值,会运用如下两条规则:

  • 如果子类没有定义指定构造器,自动继承超类的所有指定构造器。
  • 如果子类提供了超类所有指定构造器的实现,则它会自动继承超类的所有便捷构造器。

指定构造器的语法和值类型的构造器的语法相同,就是init方法。而便捷构造器是在init之前使用关键词convenience。下面使用一个示例来演示和实践指定构造器和便捷构造器用法。

class Vehicle{
    var name:String
    
    init(name:String) {
        self.name = name
    }
    convenience init() {
        self.init(name:"匿名")
    }
}
class Bike:Vehicle{
    var wheel:Int
    init(name:String,wheel:Int) {
        self.wheel = wheel
        super.init(name: name)
    }
    override convenience  init(name:String){
        self.init(name:name,wheel:2)
    }
}
class Car:Bike{
    var carNo:String = "88888"
    var description: String {
        let output = "Name:\(name)\t Wheel:\(wheel)\t No:\(carNo)"
        return output
    }
}
let car1 = Car()
print(car1.description)

let car2 = Car(name: "小白")
print(car2.description)

var car3 = Car(name: "小白", wheel: 4)
car3.carNo = "R8V23"
print(car3.description)

上面示例中,Vehicle定义了一个指定构造器init(name:String)和一个便捷构造器init(),Bike中定义了一个指定构造器init(name:String,wheel:Int)调用了Vehicle中的指定构造器,还覆盖定义了便捷构造器init(name:String),同时自动继承了Vehicle的便捷构造器init()。Car自动继承了Bike中的所有构造器。

代码执行结果如下图所示。

示例代码

https://github.com/99ios/23.16