结构器

运用结构器来完结结构进程,结构器能够看做是用来创立新实例的特别办法,与OC的结构器不同,之前是先调用父类的init再写自己的, 可是到了Swift里面, 咱们却先初始化自己, 再初始化父类, 是相反的,swift中结构器无需回来值,首要任务是保证新实例在第一次运用前完结正确的初始化


存储特点赋初始化值

  • 结构体创立实例时,有必要为一切存储类型设置初始值
  • 能够在结构器中设置, 也能够在声明特点的时分就给定
  • 为存储特点设置默许值或在结构器中赋值时,它们的值是直接被设置的,不会触发任何特点观察者
界说一个不带参数的结构器init()
//类
class Student {
    var name:String
    var age = 12  //默许特点值(假如一个特点总是运用相同的初始值,那么为其设置一个默许值比每次都在结构器中赋值要好。两种办法的效果是相同的,只不过运用默许值让特点的初始化和声明结合得更紧密。运用默许值能让你的结构器更简洁、更明晰,且能经过默许值主动推导出特点的类型)
    init() {
        name = "xiaoming" //将存储特点name的值初始化为12
    }
}
//结构体
struct People {
    var work:String
    init() {
        work = "teacher"
    }
}

自界说结构进程

经过输入参数和可选类型的特点来自界说结构进程

初始化参数

外部姓名为stuAge、stuName,内部姓名为ageInt、stuAge,将参数值保存在特点name、age中

class Student: NSObject {
    var age:Int
    var name:String
    init(stuAge ageInt:Int,stuName nameStr:String) {
        age = ageInt
        name = nameStr
    }
}
let stu = Student.init(stuAge: 12, stuName: "xiaoming")
参数的内部称号和外部称号

在界说结构器时没有供给参数的外部姓名,Swift 会为结构器的每个参数主动生成一个跟内部姓名相同的外部名,内部参数为ageInt、nameStr,在办法调用中能够看到外部参数也是ageInt、nameStr。

class Student: NSObject {
    var age:Int
    var name:String
    init(ageInt:Int,nameStr:String) {
        age = ageInt
        name = nameStr
    }
}
let stu = Student.init(ageInt: 12, nameStr: "xiaoming")
不带外部名的结构器参数

不希望为结构器的某个参数供给外部姓名,你能够运用下划线 (_) 来显式描述它的外部名

class Student: NSObject {
    var name:String
    init(_ nameStr:String) {
        name = nameStr
    }
}
let stu = Student.init("xiaoming")
可选特点类型

假如界说的类型包括一个逻辑上允许取值为空的存储型特点,无论是由于它无法在初始化时赋值,还是由于它在之后某个时刻点能够赋值为空,你都需求将它界说为可选类型。可选类型的特点将主动初始化为nil,表明这个特点是有意在初始化时设置为空的。

class Student: NSObject {
    var name:String
    var age:Int?  //主动赋值为nil
    init(_ nameStr:String) {
        name = nameStr
    }
}
let stu = Student.init("xiaoming")
stu.age = 12
print(stu.name,stu.age!)
结构进程中常量特点的修正

能够在结构进程中的任意时刻点给常量特点赋值,只要在结构进程结束时是一个确认的值,一旦常量特点被赋值,它将永久不行更改。

class Student: NSObject {
    let age:Int
    init(_ ageInt:Int) {
        age = ageInt
    }
}
let stu = Student.init(12)

默许结构器

假如结构体或类的一切特点都有默许值,一起没有自界说的结构器,那么系统会给结构体或类设置一个默许的结构器,这个默许结构器创立的实例目标,其目标的一切特点值都为其默许值

class Student: NSObject {
    var age:Int = 0
    var name:String?
    var hobby = ""
}
let stu = Student()
 //由于Student类中的一切特点都有默许值,它将主动取得一个能够为一切特点设置默许值的默许结构器(尽管代码中没有显式为name特点设置默许值,但由于name是可选字符串类型,它将默许设置为nil)。上面例子中运用默许结构器创造了一个Student类的实例,并将其赋值给常量stu。
结构体默许初始化

假如结构体没有供给自界说结构器,它们将主动取得一个逐一成员结构器,即便结构体的存储特点没有默许值
逐一成员结构器经过与成员特点名相同的参数名进行传值来完结对成员特点的初始化赋值

struct Size {
    var w = 0.0
    var h = 0.0
}
let s = Size.init(w: 2.0, h: 2.0) //结构体Size主动取得了一个逐一成员结构器init(w:, h: )

值类型的结构器署理

  • 结构器能够经过调用其它结构器来完结实例的部分结构进程,这一进程称为结构器署理
  • 结构署理对值类型和引证类型来说不太相同, 值类型由于不支持承继, 所以只会用自己写的结构器来署理, 然后相对更简略. 类则会有从父类承继结构器的状况要考虑, 不过还是那句话, 一切存储特点在结构器中都完结初始化就能够.
  • 对于值类型,你能够运用self.init在自界说的结构器中引证相同类型中的其它结构器。并且你只能在结构器内部调用self.init
  • 假如你为某个值类型界说了一个自界说的结构器,你将无法拜访到默许结构器(假如是结构体,还将无法拜访逐一成员结构器)
struct Size {
    var width = 0.0  // 悉数有默许值, 会生成2个结构器
    var height = 0.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)//仅仅简略地将origin和size的参数值赋给对应的存储型特点
    {
        self.origin = origin
        self.size = size
    }
    init(center:Point,size:Size)//先经过center和size的值计算出origin的坐标,然后再调用(或者说署理给)init(origin:size:)结构器来将新的origin和size值赋值到对应的特点中
    {
        let originX = center.x-(size.width / 2)
        let originY = center.y-(size.height / 2)
        // 结构器署理
        self.init(origin: Point.init(x: originX, y: originY), size: size)
    }
}
let rect1 = Rect.init()
let rect2 = Rect.init(center: Point.init(x: 1.0, y: 1.0), size: Size.init(width: 6.0, height: 6.0))
let rect3 = Rect.init(center: Point.init(x: 2.0, y: 2.0), size: Size.init(width: 6.0, height: 6.0))

类的结构进程

  • 类里面的一切存储型特点,包括一切承继自父类的特点,都有必要在结构进程中设置初始值
  • Swift 为类类型供给了两种结构器来保证实例中一切存储型特点都能取得初始值,它们分别是指定结构器便当结构器
  • 指定结构器 是类中最首要的结构器。每个类至少有一个指定结构器,一个指定结构器有必要真实完结一切存储特点的初始化,并依据父类链往上调用父类的结构器来完结父类的初始化
  • 便当结构器 是类中比较次要的。你能够界说便当结构器来调用同一个类中的指定结构器,并为其参数供给默许值
指定结构器
class People: NSObject {
    var name:String
    init(name:String) {
        self.name = name
    }
}
便当结构器(在init关键字之前放置convenience关键字)
class People: NSObject {
    var name:String
    init(name:String) {
        self.name = name
    }
    convenience override init(){
        self.init(name: "xiaoming")
    }
}
类的结构器署理规矩(类的指定结构办法和便当结构办法的彼此调用规矩)
  • 指定结构器有必要调用其父类的指定结构器(指定结构器只能调用指定结构器)
  • 便当结构器有必要调用同类中界说的其他结构器
  • 便当结构器有必要终究导致一个指定结构器被调用

Swift 构造器

class Student {
    var age : Int
    init(){
        age = 10
    }
    convenience init(a:Int){
        self.init()
        age = a
        print(self)  // 打印两次, 第一次是Student, 第二次是Boy
    }
}
class Boy : Student {
    var name: String
    override init(){
        name = "boy"
        super.init()
    }
}
var stu = Student(a: 2)
var boy = Boy(a: 2)
两段式结构进程
  • 一个目标的内存只要在其一切存储型特点确认之后才干彻底初始化
  • 类初始化有两阶段
    第一阶段,在类中的每个存储特点分配一个初始值
    第二阶段,每个类的实例在被运用之前进一步界说其存储的特点
第一阶段
• 某个指定结构器或便当结构器被调用
• 完结新实例内存的分配,但此刻内存还没有被初始化
• 指定结构器保证其地点类引入的一切存储型特点都已赋初值。存储型特点所属的内存完结初始化
• 指定结构器将调用父类的结构器,完结父类特点的初始化
• 这个调用父类结构器的进程沿着结构器链一向往上履行,直到到达结构器链的最顶部
• 当到达了结构器链最顶部,且已保证一切实例包括的存储型特点都现已赋值,这个实例的内存被认为现已彻底初始化
第二阶段
• 从顶部结构器链一向往下,每个结构器链中类的指定结构器都有机会进一步定制实例。结构器此刻能够拜访self修正它的特点并调用实例办法等等
• 终究,任意结构器链中的便当结构器能够有机会定制实例和运用self
class Student {
    var type:String
    init() {
        type = "学生"
    }
}
class Boy: Student {
    //第一阶段:初始化存储特点
    var age = 0
    override init() {
        //第一阶段:初始化父类
        super.init()
        //第二阶段:子类自界说
        age = 10
    }
}
let s = Boy()
print(s.type) //学生
print(s.age)  //10
当你调用Boy()时
1.首要会调用super.init()初始化父类,这时Student类的特点在本身的指定结构器中被初始化完结
2.一旦父类初始化完结,就能够初始化子类的特点,并且能够子类定制特点,这儿个性化设置age =10
  • Swift的编译器履行四个有用的安全检查,以保证完结两阶段初始化而不会呈现过错:
    1.指定结构器有必要保证它地点类引入的一切特点都有必要先初始化完结,之后才干将其它结构任务向上署理给父类中的结构器。
    2.指定结构器有必要先调用父类结构器,然后再为承继的特点设置新值。假如没这么做,指定结构器赋予的新值将被父类中的结构器所掩盖。
    3.便当结构器有必要先调用同一类中的其它结构器,然后再为任意特点赋新值。假如没这么做,便当结构器赋予的新值将被同一类中其它指定结构器所掩盖。
    4.结构器在第一阶段结构完结之前,不能调用任何实例办法,不能读取任何实例特点的值,不能引证self作为一个值

结构器的承继和重写

  • 跟 OC 中的子类不同,Swift 中的子类默许状况下不会承继父类的结构器,由于Swift不像OC会给特点默许值。Swift 的这种机制能够避免一个父类的简略结构器被一个更精密的子类承继,并被过错地用来创立子类的实例
  • 假如子类的指使结构器和父类相同, 也要用override来润饰. 可是, 假如子类的指使结构器与父类的便当结构器相同, 那么父类的便当结构器永久都不会被子类调用到, 所以这种状况是不需求写override的.
结构器的主动承继
  • 子类在默许状况下不会承继父类的结构器,可是假如满意特定条件就能够

    • 假如子类没有界说任何指定结构器,它将主动承继一切父类的指定结构器
    • 子类供给了悉数的父类指定结构器而不是从状况1取得的, 即便是供给了一部分完结, 那么它将主动承继一切的父类便当结构器. (个人认为, 调用完结的那一个指使结构器的便当结构器都能够, 其余的不行能够更智能, 而且也不会出问题)
class ClassA {
    init(a:Int){
    }
    init(b:Float) {
    }
    convenience init(c:Int){
    }
    convenience init(d:Float){
    }
}
class ClassB: ClassA {
    override init(a: Int) {
        super.init(a: a)
    }
    override init(b: Float) {
        super.init(b: b)
    }
}

ClassB 中完结了 ClassA 中一切的指定结构器,那么在初始化 ClassB 目标的时分,挑选办法如下

Swift 构造器

class ClassB: ClassA {
    override init(a: Int) {
        super.init(a: a)
    }
}

ClassB 中完结了 ClassA 中单个的指定结构器,那么在初始化 ClassB 目标的时分,挑选办法如下

Swift 构造器


可失利结构器

  • 可失利结构器:有时分结构器需求回来失利,比如给结构器传入无效的参数值,或短少某种所需的外部资源,又或是不满意某种必要的条件
  • 可失利的结构器声明:init?
    留意:可失利结构器的参数名和参数类型,不能与其它非可失利结构器的参数名,及其参数类型相同
  • 结构失利, 自然便是回来nil了, 所以可失利的结构器回来值是Optional的, 在运用的时分要留意拆包.

值类型和引证类型在处理失利结构的时分有些许不相同

  • 值类型的
struct Animal {
    let species:String
    init?(species:String) {
        if species.isEmpty{
            return nil
        }
        self.species = species
    }
}
let a = Animal.init(species: "giraffe")
print(type(of:a)) //Optional<Animal>
enum TemperatureUnit {
    case Celsius,Fahrenheit
    init?(symbol:Character){
        switch symbol {
        case "C":
            self = .Celsius
        case "F":
            self = .Fahrenheit
        default:
            return nil
        }
    }
}
let t0 = TemperatureUnit.init(symbol: "F") //Fahrenheit
let t1 = TemperatureUnit.init(symbol: "T")  //nil
  • 类类型的(只要在一切的存储特点都赋予了初值之后才干出发失利结构)
class Product{
    let name:String!
    init?(name:String) {
        self.name = name
        if name.isEmpty {
            return nil
        }
    }
}
let p0 = Product.init(name: "")  //nil
let p1 = Product.init(name: "A")  //Product

结构失利的传递

类,结构体,枚举的可失利结构器能够传递到其他可失利结构器,而且子类的可失利结构器也能传递到父类的可失利结构器,假如你署理到的其他可失利结构器触发结构失利,整个结构进程将立即停止,接下来的任何结构代码不会再被履行

class ClassA{
    let str:String!
    init?(str:String) {
        if str.isEmpty{
            return nil
        }
        self.str = str
    }
}
class ClassB:ClassA{
    let b:Float
    init?(a:String,b:Float) {
        if b<1 {
            return nil
        }
        self.b = b
        super.init(str: a)
    }
}
let c = ClassB.init(a: "1", b: 0)   //nil
let d = ClassB.init(a: "", b: 1)   //nil
let e = ClassB.init(a: "1", b: 1)  //ClassB

重写可失利结构器

能够用一个不行失利的结构器从头父类可失利结构器,可是不能反过来,重写父类结构器时,回来值要强制解包

class ClassA{
    var str:String
    init?(str:String) {
        self.str = str
        if str.isEmpty{
            return nil
        }
    }
}
class classC:ClassA{
    var c:String
    override init(str: String) {
        c = str
        super.init(str: str)!
    }
}
let cc = classC.init(str:"d")  //classC

init!不行失利结构器

init?需求解包,init!直接运用
不论运用哪种方式,都需求判空


必要结构器

假如结构器用required来润饰,那么意味着子类有必要重写该父类的结构器

class ClassA{
    var str:String
    required init(str:String){
        self.str = str
    }
}
class classC:ClassA{
    var c:String
    required init(str: String) {
        c = str
        super.init(str: c)
    }
}
let cc = classC.init(str: "c") //classC

假如子类承继的结构器能满意必要结构器的要求,则无须在子类中显式供给必要结构器的完结

class ClassA{
    var str:String
    required init(){
        str = "0"
    }
}
class classC:ClassA{
    var c = "1"
}
let cc = classC.init() //classC