Swift中的构造方法

  结构体和类在创建实例的过程中需要进行一些初始化的工作,这个过程被称之为构造过程。同样,这些实例在使用完成之后需要做一些清除工作,这个过程被称之为析构过程。下面,我们来学习一下Swift中的构造和析构。

  1、构造函数

  结构体和类在实例化的过程中会调用init()方法,而这个方法被称之为构造函数。与Objective-C中的构造函数不同,Swift中构造函数没有返回值,可以重载。通常情况下,在定义一个类或者结构体时,不需要我们编写构造函数,系统会提供一个默认的构造函数:

class Person {
    var age: Int = 0
    var name: String?

    lazy var stu: Student = Student()
}

struct Student {
    var no: Int = 0
    var sex: String?
}

// 实例化一个Person对象
let p = Person()
p.age = 20
p.name = "Lilei"
p.stu.no = 1701
p.stu.sex = "Male"

  以前在Objective-C中,我们实例化一个类时,一般会明确调用alloc和init方法。比如说,创建一个UIView对象:

UIView *view = [[UIView alloc] init];

  但是,在Swift中,如果要创建一个UIView对象,好像不用调用任何方法,直接使用类型加小括号就可以了:

let view = UIView()

  实际上,在Swift中创建一个对象,仍然需要调用方法,而小括号就代表着方法的调用。在上面的代码中,我们实际上调用了系统默认的init()方法。以上面的Person类为例,它实际上应该是类似于下面这样的:

class Person {
    var age: Int = 0
    var name: String?

    lazy var stu: Student = Student()

    init() {

    }
}

struct Student {
    var no: Int = 0
    var sex: String?

    init() {

    }
}

  2、构造函数与存储属性的初始化

  我们都知道,存储属性在定义的时候需要初始化,否则编译器会报错。但是实际上,除了可以在定义的时候初始化之外,还可以在构造函数中对其进行初始化:

class Person {
    var age: Int
    var name: String

    lazy var stu: Student = Student(no: 0, sex: "Male")

    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
}

struct Student {
    var no: Int
    var sex: String

    init(no: Int, sex: String) {
        self.no = no
        self.sex = sex
    }
}

// 在实例化的过程中,通过构造函数对它的属性进行初始化
let p = Person(age: 20, name: "LeBron")
p.stu.no = 1701
p.stu.sex = "Femal"

  以上面的Person类为例,其属性age和name在定义的时候没有被初始化,但是在实例化的时候,我们通过构造函数分别给它们初始化为20和LeBron。构造函数只能初始化存储属性(不管常量还是变量都可以),不能初始化计算属性(因为计算属性不需要存储数据)和静态属性(因为静态属性跟具体的实例无关)。

  3、构造函数重载

  在了解什么是构造函数重载之前,我们先来了解一下什么叫做函数重载。函数重载就是指函数名(或者沿袭Objective-C中叫方法也行)相同,但是参数不同。参数不同又包括参数标签不同、参数个数不同和参数类型不同。Objective-C中没有方法重载的概念,因为在Objective-C中调用方法的本质是发送消息,如果方法名相同,系统在发送消息时,就不知道该把消息发送给谁。构造函数重载,就是指同一个类或者结构体中可以有多个init()函数,但是它们的参数不同。比如说:

// 类的构造函数
class Person {
    var age: Int
    var name: String

    // 构造函数1
    init() {
        self.age = 0
        self.name = ""
    }

    // 构造函数2
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }

    // 构造函数3
    init(A age: Int, N name: String) {
        self.age = age
        self.name = name
    }
}

// 调用构造函数1
let p1 = Person()
p1.age = 33
p1.name = "James"
print("age = \(p1.age), name = \(p1.name)")  // age = 33, name = James

// 调用构造函数2
let p2 = Person(age: 33, name: "Anthony")
print("age = \(p2.age), name = \(p2.name)")  // age = 33, name = Anthony

// 调用构造函数3
let p3 = Person(A: 35, N: "Wade")
print("age = \(p3.age), name = \(p3.name)")  // age = 35, name = Wade

  上面的Person类中定义了3个构造函数,它们之间是重载的关系,在实例化的过程中,通过调用不同的构造函数来创建对象。

  4、构造函数代理

  有时候为了减少代码重复,在定义构造函数时,可以在一个构造函数中调用另外一个构造函数,这种情况被称之为构造函数代理。比如说:

class Person {
    var age: Int
    var name: String

    // 构造函数1
    convenience init() {
        // 调用了构造函数2
        self.init(age: 0, name: "")
    }

    // 构造函数2
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }

    // 构造函数3
    init(A age: Int, N name: String) {
        self.age = age
        self.name = name
    }
}

  在上面的代码中,我们在构造函数1中调用了构造函数2,这种情况就是构造函数代理。在结构体中,构造函数调用其它构造函数时,可以直接调用,前面不用加关键字convenience。但是,由于类具有继承关系,类里面的构造函数代理又相对复杂一点。

  如果构造函数代理是发生在同一个类的内部,那么这种构造函数便称之为便利构造函数,比如说像上面那种情况就是便利构造函数;如果构造函数代理是发生在子类中,那么子类在构造过程中需要调用父类的构造函数,初始化父类的存储属性,这种构造函数称之为指定构造函数。比如说:

class Person {
    var age: Int
    var name: String

    init() {
        self.age = 0
        self.name = ""
    }

    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
}

class Student: Person {

    var no: Int
    var height: Double

    // 构造函数
    init(age: Int, name: String, no: Int, height: Double) {

        self.no = no
        self.height = height

        // 调用父类的构造函数,对父类中的属性进行初始化
        super.init(age: age, name: name)
    }

    // 便利构造函数
    convenience override init(age: Int, name: String) {

        // 初始化no和height
        self.init(age: age, name: name, no: 0, height: 0.0)
    }
}

// 实例化Student
let stu = Student(age: 20, name: "Anthony", no: 1701, height: 1.72)

  子类中构造函数的代理涉及到了继承,所以相关代码的解释我准备放在继承中进行。

  5、析构函数

  析构过程与构造过程相反,析构在实例释放的时候需要清除一些资源。析构过程会调用deinit函数。deinit函数又被称为析构函数,它没有返回值,没有参数,也不需要小括号。析构函数不能重载。析构函数的具体实例如下:

// 类的构造函数
class Person {
    var age: Int
    var name: String

    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }

    convenience init(A age: Int, N name: String) {
        self.init(age: age, name: name)
    }

    deinit {
        self.age = 0
        self.name = ""
    }
}

var p: Person? = Person(age: 20, name: "James")
print("age = \(p!.age), name = \(p!.name)")

// 调用析构函数,释放资源
p = nil

  析构函数的调用使得实例被赋值为nil,表示实例需要释放资源。p = nil是触发析构函数调用的条件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值