Swift-构造过程

作者: wg刚 | 来源:发表于2019-01-15 18:40 被阅读0次

    最近项目使用的是OC,后头看之前用Swift开发的一个项目时,发现很多细节都忘记了😭😭。
    为了回忆和以后方便查看,现在根据官方文档swift编程语言,做下笔记。

    构造过程是使用类、结构体或枚举类型的实例之前的准备过程。在新实例可用前必须执行这个过程,具体操作包括设置实例中每个存储型属性的初始值和执行其他必须的设置或初始化工作。

    与 Objective-C 中的构造器不同,Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。

    1、存储属性的初始赋值

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

    当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观察者。

    • 构造器

    构造器在创建某个特定类型的新实例时被调用。它的最简形式类似于一个不带任何参数的实例方法,以关键字init命名

    init() {
        // 在此处执行构造过程
    }
    

    例如:定义了一个用来保存华氏温度的结构体Fahrenheit,它拥有一个Double类型的存储型属性temperature

    这个结构体定义了一个不带参数的构造器init,并在里面将存储型属性temperature的值初始化为32.0。

    struct Fahrenheit {
        var temperature: Double
        init() {
            temperature = 32.0
        }
    }
    var f = Fahrenheit()
    print("The default temperature is \(f.temperature)° Fahrenheit")
    // 打印 "The default temperature is 32.0° Fahrenheit"
    
    • 自定义构造过程

    你可以通过输入参数和可选类型的属性来自定义构造过程,也可以在构造过程中修改常量属性。

    构造参数

    自定义构造过程时,可以在定义中提供构造参数,指定所需值的类型和名字。构造参数的语法和函数的参数相同。

    例如:下面例子中定义了一个包含摄氏度温度的结构体Celsius

    构造器拥有一个构造参数,其外部名字为fromFahrenheit,内部名字为fahrenheit

    struct Celsius {
        var temperatureInCelsius: Double
        init(fromFahrenheit fahrenheit: Double) {
            temperatureInCelsius = (fahrenheit - 32.0) / 1.8
        }
    }
    let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
    // boilingPointOfWater.temperatureInCelsius 是 100.0
    
    • 默认构造器

    如果结构体或类的所有属性都有默认值,同时没有自定义的构造器,那么 Swift 会给这些结构体或类提供一个默认构造器(default initializers)。这个默认构造器将简单地创建一个所有属性值都设置为默认值的实例。

    例如:创建了一个类ShoppingListItem,它封装了购物清单中的某一物品的属性:名字(name)、数量(quantity)和购买状态 purchase state

    由于ShoppingListItem类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个可以为所有属性设置默认值的默认构造器

    class ShoppingListItem {
        var name: String?
        var quantity = 1
        var purchased = false
    }
    var item = ShoppingListItem()
    
    结构体的逐一成员构造器

    如果结构体没有提供自定义的构造器,它们将自动获得一个逐一成员构造器,即使结构体的存储型属性没有默认值。

    例如:

    struct Size {
        var width = 0.0, height = 0.0
    }
    let twoByTwo = Size(width: 2.0, height: 2.0)
    
    如果你为某个值类型定义了一个自定义的构造器,你将无法访问到默认构造器(如果是结构体,还将无法访问逐一成员构造器)。

    假如你希望默认构造器、逐一成员构造器以及你自己的自定义构造器都能用来创建实例,可以将自定义的构造器写到扩展(extension)中,而不是写在值类型的原始定义中。

    • 类的继承和构造过程

    类里面的所有存储型属性——包括所有继承自父类的属性——都必须在构造过程中设置初始值。
    Swift 为类类型提供了两种构造器来确保实例中所有存储型属性都能获得初始值,它们分别是指定构造器和便利构造器。

    指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。
    每一个类都必须拥有至少一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件。

    便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。

    1、指定构造器和便利构造器的语法

    类的指定构造器写法跟值类型简单构造器一样

    init(parameters) {
        statements
    }
    

    便利构造器也采用相同样式的写法,但需要在init关键字之前放置convenience关键字,并使用空格将它们俩分开

    convenience init(parameters) {
        statements
    }
    
    2、类的构造器代理规则

    为了简化指定构造器和便利构造器之间的调用关系,Swift 采用以下三条规则来限制构造器之间的代理调用:

    规则 1:指定构造器必须调用其直接父类的的指定构造器。
    规则 2:便利构造器必须调用同类中定义的其它构造器。
    规则 3:便利构造器必须最终导致一个指定构造器被调用。

    如下图:

    3、两段式构造过程

    Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性被引入它们的类指定一个初始值。当每个存储型属性的初始值被确定后,第二阶段开始,它给每个类一次机会,在新实例准备使用之前进一步定制它们的存储型属性。

    Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能不出错地完成

    安全检查 1:
    指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。
    安全检查 2:
    指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。
    安全检查 3:
    便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。如果没这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。
    安全检查 4:
    构造器在第一阶段构造完成之前,不能调用任何实例方法,不能读取任何实例属性的值,不能引用self作为一个值。

    4、构造器的继承和重写

    当你重写一个父类的指定构造器时,你总是需要写override修饰符,即使你的子类将父类的指定构造器重写为了便利构造器。
    如果你编写了一个和父类便利构造器相匹配的子类构造器,不需要加override前缀。

    class Vehicle {
        var numberOfWheels = 0
        var description: String {
            return "\(numberOfWheels) wheel(s)"
        }
    }
    

    Vehicle类只为存储型属性提供默认值,而不自定义构造器。因此,它会自动获得一个默认构造器.

    let vehicle = Vehicle()
    print("Vehicle: \(vehicle.description)")
    // Vehicle: 0 wheel(s)
    
    

    下面例子中定义了一个Vehicle的子类Bicycle

    class Bicycle: Vehicle {
        override init() {
            super.init()
            numberOfWheels = 2
        }
    }
    

    子类Bicycle定义了一个自定义指定构造器init()。这个指定构造器和父类的指定构造器相匹配,所以Bicycle中的指定构造器需要带上override修饰符。
    Bicycle的构造器init()以调用super.init()方法开始,这个方法的作用是调用Bicycle的父类Vehicle的默认构造器。这样可以确保Bicycle在修改属性之前,它所继承的属性numberOfWheels能被Vehicle类初始化。在调用super.init()之后,属性numberOfWheels的原值被新值2替换。

    5、构造器的自动继承

    子类在默认情况下不会继承父类的构造器。但是如果满足特定条件,父类构造器是可以被自动继承的。在实践中,这意味着对于许多常见场景你不必重写父类的构造器,并且可以在安全的情况下以最小的代价继承父类的构造器。

    假设你为子类中引入的所有新属性都提供了默认值,以下 2 个规则适用:
    规则 1
    如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
    规则 2
    如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承所有父类的便利构造器。

    • 可失败构造器

    如果一个类、结构体或枚举类型的对象,在构造过程中有可能失败,则为其定义一个可失败构造器。这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。

    为了妥善处理这种构造过程中可能会失败的情况。你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在init关键字后面添加问号(init?)。

    可失败构造器会创建一个类型为自身类型的可选类型的对象。你通过return nil语句来表明可失败构造器在何种情况下应该“失败”。
    严格来说,构造器都不支持返回值。因为构造器本身的作用,只是为了确保对象能被正确构造。因此你只是用return nil表明可失败构造器构造失败,而不要用关键字return来表明构造成功。

    struct Animal {
        let species: String
        init?(species: String) {
            if species.isEmpty { return nil }
            self.species = species
        }
    }
    
    let someCreature = Animal(species: "Giraffe")
    // someCreature 的类型是 Animal? 而不是 Animal
    
    if let giraffe = someCreature {
        print("An animal was initialized with a species of \(giraffe.species)")
    }
    // 打印 "An animal was initialized with a species of Giraffe"
    

    相关文章

      网友评论

        本文标题:Swift-构造过程

        本文链接:https://www.haomeiwen.com/subject/pggarqtx.html