美文网首页
Swift中的构造方法

Swift中的构造方法

作者: 落叶刺客 | 来源:发表于2017-05-24 20:08 被阅读113次

    结构体和类在创建实例的过程中需要进行一些初始化的工作,这个过程被称之为构造过程。同样,这些实例在使用完成之后需要做一些清除工作,这个过程被称之为析构过程。下面,我们来学习一下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是触发析构函数调用的条件。

    相关文章

      网友评论

          本文标题:Swift中的构造方法

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