zoukankan      html  css  js  c++  java
  • Swift13-构造过程

    存储属性的初始赋值、自定义构造过程、默认构造器、值类型的构造器代理、类的继承和构造过程、可失败构造器、必要构造器、通过闭包或函数设置属性的默认值。

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

    通过构造器来实现构造过程,这些构造器可以看作是用来创建特定类型新实例的特殊方法。Swift的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化

    类的实例也可以通过定义析构器在实例释放之前执行特定的清除工作。

    存储属性的初始赋值:

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

    可以在构造器中为存储属性赋初值,也可以在定义属性时为其设置默认值。

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

    结构体和枚举:有默认的逐一构造器,相当于为存储属性设置默认值,不会触发属性观察器和计算型赋值的setter语句。类没有逐一构造器。

    结构体、枚举和类的其它构造器会触发计算型存储属性的setter语句。

    所有的默认值、构造器设置初始值都不会触发属性观察器。

    构造器:

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

    class Base{

      init{

        //

      }

    }

    默认属性值:

    可以在构造器中为存储型属性设置初始值,也可以在属性声明时为其设置默认值。

    自定义构造过程:

    构造参数:init( label para:type,...){...}

    参数内部的名称和外部名称:

    不带外部名的构造器参数:_

    可选属性类型:

    可选类型,可能为空nil。可选类型的属性将自动初始化为nil,表示这个属性是有意在初始化时设置为空的。

    class Base{

      var a:Int?

      init(){//空执行}

    }

    var t=Base()  //此时t.a为空nil

    构造过程中常量属性的修改:

    可以在构造过程中的任意时间点给常量属性指定一个值,只要在构造过程结束时常量属性是一个确定的值。一旦常量属性被赋值,它将永远不可改变。

    常量属性只能在默认值和构造器初始化其一赋值,之后不可改变。

    默认构造器:

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

    结构体的逐一成员构造器:

    如果结构体没有提供自定义的构造器,它们将自动获得一个逐一成员构造器。(但如果结构体存在构造器,则没有逐一成员构造器可用了)

    值类型的构造器代理:

    构造器可以通过调用其它构造器来完成实例的部分构造过程。这称为构造器代理。

    构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给自己的其它构造器。类则不同,它可以继承自其它类,这意味着类有责任保证其所有继承的存储属性在构造时也能正确的初始化。

    对于值类型,可以使用self.init在自定义的构造器中引用相同类型中的其它构造器。并且只能在构造器内部调用self.init。

    (注意:如果想要同时使用默认构造器、逐一成员构造器以及自定义的构造器来创建实例,可以将自定义的构造器写到扩展(extension)中,而不是值原始定义中)

    class Base{

      var a:Int=1,b=2

      init(_ a:Int){

        self.a=a

      }

      init(_ a:Int, _ b:Int){

        self.init(a)

        self.b=b

      }

    }

    //值类型的构造器代理

    类的继承和构造过程

    类里面的所有存储属性。包括继承自父类的属性,都必须在构造过程中设置初始值。

    Swift为类类型提供了两种构造器来确保实例中所有存储属性都能获得初始值,它们分别是指定构造器和便利构造器。

    指定构造器和便利构造器:

    指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。

    每一个类都必须有至少一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件。

    便利构造器是类中比较次要的、辅助型的构造器。可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。可以定义便利构造器来创建一个特殊用途或特定输入值的实例。

    指定构造器和便利构造器的语法:

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

    init(parameters){

      statements

    }

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

    convenience init(parameters){

      statements

    }

    类的构造器代理规则:

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

    1)指定构造器必须调用其直接父类的指定构造器

    2)便利构造器必须调用同类中定义的其它构造器。

    3)便利构造器必须最终导致一个指定构造器被调用。

    两段式构造过程:

    存储属性--->引入该属性的类(每个类都有其存储属性的默认值或指定构造器)

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

    两段式构造过程可以防止属性值在初始化之前被访问,也可以防止属性被另外一个构造器意外地赋予不同的值。

    类不能在一个实例化过程中调用两次及以上的指定构造器,但可以是一个或多个遍历构造器与一个指定构造器的组合

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

    1)指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器

    满足类的构造代理规则中的:一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。

    2)指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。

    3)便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值,如果没有这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。

    4)构造器在第一阶段构造完成之前,不能调用任何实例方法,不能读取任何实例属性的值,不能引入self作为一个值。

    类实例在第一阶段结束以前并不是完全有效的。只有第一阶段完成后,该实例才会成为有效实例,才能访问属性和调用方法。

    构造器的继承和重写:

    跟OC中的子类不同,Swift中的子类默认情况下不会继承父类的构造器。Swift的这种机制可以防止一个父类的简单构造器被一个更精细的子类继承,并被错误地用来创建子类的实例。

    假如希望自定义的子类中能提供一个或多个跟父类相同的构造器,可以在子类中提供这些构造器的自定义实现。

    当你在编写一个和父类中指定构造器相匹配的子类构造器时,实际上是在重写父类的这个指定的构造器。因此,必须在定义子类构造器时带上override修饰符。即使你重写的是系统自动提供的默认构造器,也需要带上override修饰符。

    正如重写属性、方法或者下标,override修饰符会让编译器去检查父类中是否有相匹配的指定构造器,并验证构造器参数是否正确。

    注意:当重写一个父类的指定构造器时,总是需要写override修饰符,即使子类将父类的指定构造器重写为了便利构造器。

    构造器的自动继承:

    子类在默认情况下不会继承父类的构造器。但是如果满足特定条件,父类构造器是可以被自动继承的

    假如为子类中引入的所有新属性都提供了默认值,以下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

      }

    }

    var t=Animal(species:"Sheep")

    print(t!.species)

    //通过可失败构造器构造的可选的该类型实例,访问时在实例名称后加上!

    var tt=Animal(species:"")

    //此时tt为nil,不能访问它的species属性

    枚举类型的可失败构造器:

    可以通过一个带一个或多个参数的可失败构造器来获取枚举类型中特定的枚举成员。如果提供的参数无法匹配任何枚举成员,则构造失败。

    enum TemperatureUnit{

      case Kelvin, Celsius, Fahrenheit

      init?(symbol:Character){

        switch symbol{

        case "K":

          self = .Kelvin

        case "C":

          self = .Celsius

        case "F":

          self = .Fahrenheit

        default:

          return nil

        }

      }

    }

    带原始值的枚举类型的可失败构造器:

    带原始值的枚举类型会自带一个可失败构造器init?(rawValue:)该可失败构造器有一个名为rawValue的参数,其类型和枚举类型的原始值类型一致,如果该参数的值能够和某个枚举成员的原始值匹配,则该构造器会构造相应的枚举成员,否则构造失败。

    enum Sheep:Character{

      case XYY="X", LYY="L", MYY="M"

    }

    let a=Sheep(rawValue:"x")

    if a == nil{.....}

    构造失败的传递:

    类、结构体、枚举的可失败构造器可以横向代理到类型中的其它可失败构造器。类似地,子类的可失败构造器也能向上代理到父类的可失败构造器。

    如果可失败构造器触发构造失败,整个构造过程将立即终止,接下来的任何构造代码不会再被执行。

    注意:可失败构造器也可以代理到其它的非可失败构造器。通过这种方式,可以增加一个可能的失败状态到现有的构造过程中。

    重写一个可失败构造器:

    如同其它的构造器,可以在子类中重写父类的可失败构造器。或者可以用子类的非可失败构造器重写一个父类的可失败构造器。这使得可以定义一个不会构造失败的子类,即使父类的构造器允许构造失败。

    注意:当用子类的非可失败构造器重写父类的可失败构造器时,向上代理到父类的可失败构造器的唯一方式是对父类的可失败构造器的返回值进行强制解包。

    可以用非可失败构造器重写可失败构造器,但反过来不行。

    可失败构造器:init?,除此之外,还可以用init!来定义,在init关键字后加一个感叹号。

    init!型的可失败构造器将会构建一个对应类型的隐式解包可选类型的对象。

    init?和init!可相互代理和重写。不过一旦init!构造失败,则会触发一个断言

    必要构造器:

    在类的构造器前添加required修饰符表明所有该类的子类都必须实现该构造器:

    class SomeClass{

      required init(){

        //构造器的实现代码

      }

    }

    在子类重写父类的必要构造器时,必须在子类的构造器前也添加required修饰符,表明该构造器要求也应用于继承链后面的子类。在重写父类中必要的指定构造器时,不需要添加override修饰符:

    class SomeSubclass:SomeClass{

      required init(){

        //

      }

    }

    如果子类继承的构造器能满足构造器的要求,则无须在子类中显式提供必要的构造器实现。

    通过闭包或函数设置属性的默认值:

    如果某个存储型属性的默认值需要一些定制或设置,可以使用闭包或全局函数为其提供定制的默认值。每当某个属性所在类型的新实例被创建时,对应的闭包或函数会被调用,而它们的返回值会当做默认值赋值给这个属性。

    这种类型的闭包或函数通常会创建一个跟属性类型相同的临时变量,然后修改它的值以满足预期的初始状态,最后返回这个临时变量,作为属性的默认值。

    class SomeClass{

      let someProperty:SomeType={

        //在这个闭包中给someProperty创建一个默认值

        //someValue必须和SomeType类型相同

        return someValue

      }()

    }

    注意闭包结尾的大括号后面接了一对空的小括号。这用来告诉Swift立即执行此闭包。如果忽略了这对括号,相当于将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。

    如果使用闭包来初始化属性,记住在闭包执行时,实例的其它部分都还没有初始化。这意味着不能在闭包里访问其它属性,即使这些属性有默认值。同样,也不能使用隐式的self属性,或者调用任何实例方法。

  • 相关阅读:
    RAID磁盘阵列介绍
    Nginx如何使用Nginx实现MySQL数据库的负载均衡
    挽救数据库性能的30条黄金法则
    mysql主从复制 (指定复制的数库或者表)
    Nginx系列之负载均衡策略
    Redis安装部署(一主二从三哨兵)
    让你的 Linux 命令骚起来
    MySQL/数据库 知识点总结
    Docker私有仓库搭建与界面化管理
    mysql sql语句修改字段名称,字段长度
  • 原文地址:https://www.cnblogs.com/cjj-ggboy/p/12851457.html
Copyright © 2011-2022 走看看