Swift类的初始化

在swift中,对象的初始化和oc中类似,分为两个阶段,一个阶段是对象的内存分配,另一个阶段是对对象中的store value进行初始化。不过第一个阶段和oc略有不同,oc在第一阶段分配内存后,会将所有的属性设为0或者null,而swift则更为灵活,可以指定一些除0以外的值。

全能初始化方法和便利初始化方法

在详细分析两段式(two-phrase)初始化之前,先了解一下swift中两种初始化方法:全能初始化方法(designated initializier)和便利初始化方法(convenience initializer)。

全能初始化方法

就像其名字一样,通过全能初始化方法来初始化一个对象后,其所有的store property都能够进行初始化。一个类可以有多个全能初始化方法,但是一般来说只有一个。只要保证全能初始化方法能够初始化所有属性即可。

class GameCharacter {
    var weapon: String
    
    init() {
        self.weapon = "fist"
    }
    
    init(weapon: String) {
        self.weapon = weapon
    }
}

如上,Person类有两个全能初始化方法,一个init(),一个是init(name: String),每个初始化方法都能够将name进行初始化。

如果不在初始化方法中对非可选值的属性进行初始化,编译器会提示未初始化的错误。

便利初始化方法

便利初始化方法则只是初始化类中部分属性。看以下代码

class Warrior: GameCharacter {
    var shield: String
    
    init(weapon: String, shield: String) {
        self.shield = shield
        super.init(weapon: weapon)
    }
    
    convenience init(shield: String) {
        self.init(weapon: "fist", shield: shield)
    }
}

Warrior有一个全能初始化方法init(weapon: String, shield: String,这个初始化方法可以初始化所有的属性,也就是shield和从父类继承来的weapon。另外还有一个便利初始化方法init(shield: String),该方法只显示指定了shield的值,而weapon的值则是提供一个默认的值。我们注意到在这个初始化方法在前面加了一个关键字convenience。这个convenience除了表明这个方法是便利初始化方法外,还有一个作用就是让编译器进行一些错误检查。

全能初始化方法和便利初始化方法之间的调用规则

全能初始化方法和便利初始化方法调用规则有三个

  • 全能初始化方法必须调用父类的全能初始化方法
  • 便利初始化方法只能调用当前类的初始化方法,不能调用父类的初始化方法
  • 便利初始化方法,最终必须调用当前类的全能初始化方法。

下图可以很直观的表明这三个规则:

image 1

类的两段式初始化

swift和oc最大的区别,除了多了很多新的特性,比如新的枚举类型,struct类型等,最明显的区别就是在编译器上面。swift的编译器在编译期间做了很多的安全检查工作,比如对于一个可能为nil的值,必须进行解包;两个不同类型的变量不能相互进行赋值(即使是Double和Int之间的也不能隐式转换)。类的初始化方法也一样。

在类的初始化方法中,有四个安全性的检查:

  • 全能初始化方法必须确保在父类被初始化之前,所有的属性被正确初始化
  • 再给被继承的属性赋值之前,必须调用父类的全能初始化方法,也就是说,父类必须被初始化之后才能给被集成的属性赋值
  • 便利初始化方法在对属性赋值之前,必须调用其它初始化方法
  • 在当前类没有被正确初始化之前(即第一阶段),不能调用类的实例方法和读取实例属性

下面这段代码说明了这四个检查

class Warrior: GameCharacter {
    var shield: String
    
    init(weapon: String, shield: String) {
        self.shield = shield // 在调用父类之前,必须对初始化本类的属性
//        attack() 编译器错误,当前类还没初始化完成,不能调用实例方法
        super.init(weapon: weapon)
        self.weapon = "sword" //在父类初始化完成后,才能对从父类集成的属性进行赋值
    }
    
    convenience init(shield: String) {
        self.init(weapon: "fist", shield: shield)
        self.shield = "silver shield" //便利初始化方法在调用其它初始化方法后,才能对属性进行赋值
    }
    
    func attack() {
        Swift.print("use \(weapon) to attack")
    }
}

如果以上四个检查任何一个不符合,编译器都会报错。

而swift的两段式初始化正式基于这四个安全检查的,下面来看看这两个阶段分别作了什么

第一阶段

  • 调用全能初始化方法或者便利初始化方法,系统分配内存,但是并未对这个类进行初始化
  • 在全能初始化方法中初始化所有属性
  • 调用父类的全能初始化方法执行前两个相同的步骤
  • 沿着继承链不断向上一直到根类为止
  • 这个时候在继承链上的类都被正确的初始化,第一阶段完成

第二阶段

  • 从根类开始向下返回,每个类的全能初始化方法都能够进行更多的对类的初始化操作,包括可以使用属性,可以调用实例方法
  • 最后,任何便利方法都有机会去对类进行更多的初始化

沿用上一个代码片段,当调用init(weapon: String, shield: String)时,第一阶段的初始化就开始了,直到GameCharacter的全能初始化方法中的self.weapon = "fist",这时候第一阶段结束。然后再沿着继承链向下,直到Warrior类的全能初始化方法中的self.weapon = "sword时,第二阶段结束。

我们可以看到,swift的二段式初始化能够保证我们在使用一个类的时候能够被正确的初始化,而且这个过程都是在编译时检查的,这样就能够确保我们在编写类的初始化代码的时候不会犯错。

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

推荐阅读更多精彩内容

  • 官方文档 初始化 Initialization是为准备使用类,结构体或者枚举实例的一个过程。这个过程涉及了在实例里...
    hrscy阅读 1,130评论 0 1
  • 初始化(Initialization) 初始化是类、结构体、枚举类型的准备过程。这个过程涉及到所有存储属性的初始化...
    泗哥阅读 5,604评论 0 3
  • 初始化 (Initialization) 自从苹果2014年发布Swift,到现在已经两年多了,而Swift也来到...
    Lebron_James阅读 1,193评论 0 0
  • 123.继承 一个类可以从另外一个类继承方法,属性和其他特征。当一个类继承另外一个类时, 继承类叫子类, 被继承的...
    无沣阅读 1,358评论 2 4
  • Eureka客户端常用配置 eureka.client.enabled=true——启用Eureka客户端,默认t...
    zlup阅读 1,447评论 0 2