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是触发析构函数调用的条件。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • 本章将会介绍 存储属性的初始赋值自定义构造过程默认构造器值类型的构造器代理类的继承和构造过程可失败构造器必要构造器...
    寒桥阅读 749评论 0 0
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young阅读 3,673评论 1 10
  • 下标脚本 下标脚本 可以定义在类、结构体和枚举这些目标中,可以认为是访问集合(collection),列表(li...
    cht005288阅读 419评论 0 0
  • 我是喜欢拿铁的,至于原因,我也说不上来,只因为我喜欢。据说拿铁咖啡是意大利浓缩咖啡(Espresso)与牛...
    简单生活16阅读 434评论 0 1
  • 初次见他是在我开的一家名为“忧温”的咖啡馆里。 来我这的人,要么成双结对,要么三五成群,要么形单影只。所以刚开始他...
    吴希道阅读 232评论 1 4