swift

swift 构造器

2019-04-30  本文已影响0人  皆为序幕_

构造器

使用构造器来实现构造过程,构造器可以看做是用来创建新实例的特殊方法,与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: )

值类型的构造器代理

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))

类的构造过程

指定构造器
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")
    }
}
类的构造器代理规则(类的指定构造方法和便利构造方法的相互调用规则)
初始化.png
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

构造器的继承和重写

构造器的自动继承
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 对象的时候,选择方法如下

所有的指定构造器.png
class ClassB: ClassA {
    override init(a: Int) {
        super.init(a: a)
    }
}

ClassB 中实现了 ClassA 中个别的指定构造器,那么在初始化 ClassB 对象的时候,选择方法如下

个别的指定构造器.png

可失败构造器

值类型和引用类型在处理失败构造的时候有些许不一样

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

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

在设置存储属性默认值时,可以用函数或者闭包来实现

class SomeClass {
    let someProperty: SomeType = {
        // 注意: 在这里不能用self, 更不能用其它的属性(即使它有默认值, 因为self还没准备好)或者该类的实例方法,执行这段代码时, 初始化都还没有进行

        // 在这个闭包中给 someProperty 创建一个默认值
        // someValue 必须和 SomeType 类型相同
        return someValue
    }()// 最后要加上(), 不然就被当做是个闭包, 而不是这个闭包的结果了
}
struct Checkerboard {
    let boardColors: [Bool] = {
        var temporaryBoard = [Bool]()
        var isBlack = false
        for i in 1...8 {
            for j in 1...8 {
                temporaryBoard.append(isBlack)
                isBlack = !isBlack
            }
            isBlack = !isBlack
        }
        return temporaryBoard
    }()
    func squareIsBlackAtRow(row: Int, column: Int) -> Bool {
        return boardColors[(row * 8) + column]
    }
}

每当一个新的Checkerboard实例被创建时,赋值闭包会被执行,boardColors的默认值会被计算出来并返回。上面例子中描述的闭包将计算出棋盘中每个格子对应的颜色,并将这些值保存到一个临时数组temporaryBoard中,最后在构建完成时将此数组作为闭包返回值返回。这个返回的数组会保存到boardColors中,并可以通过工具函数squareIsBlackAtRow来查询:

上一篇 下一篇

猜你喜欢

热点阅读