构造器知识点

为什么要有构造器:为类中自身和继承来的存储属性赋初值。
一、两种构造器-指定构造器和便利构造器
指定构造器:类中必备的构造器,为所有的属性赋初值。(有些子类可能不需要显示声明,因为默认从基类继承了)
便利构造器:类中的辅助构造器,通过调用指定构造器为属性赋初值。(仅在必要的时候声明)
举例

  1. class Food {
  2. var name: String
  3. init(name: String) {
  4. self.name = name
  5. }
  6. convenience init() {
  7. self.init(name: "[Unnamed]")
  8. }
  9. }

便利构造器通过convenience关键字声明,可以看到,便利构造器是通过调用指定构造器来进行构造的。这也就是一个关键的概念:横向代理。
何为代理:就是让别人帮你干活
二、构造过程中的规则
(一)构造器链就是调用构造器的顺序
规则如下:
1.1、指定构造器必须调用其父类的指定构造器
1.2、便利构造器必须调用同一类中的指定构造器
1.3、便利构造器必须最后以调用一个指定构造器而结束
总得来说一句话:便利构造器横向代理,指定构造器向上代理。
举个例子:

  1. class Base{
  2. var baseVar:String
  3. init(baseInput:String){
  4. baseVar = baseInput
  5. }
  6. convenience init(){
  7. self.init(baseInput:"")
  8. }
  9. }
  10. class Sub:Base{
  11. var subVar:String;
  12. init(subInput:String,baseInput:String){
  13. subVar = subInput
  14. super.init(baseInput:baseInput)//这里是规则1.1
  15. }
  16. convenience init(conSubInput:String){
  17. self.init(subInput:conSubInput,baseInput:"")//这里是规则1.2
  18. }
  19. convenience init(){
  20. self.init(conSubInput:"")//这里是规则1.3,因为调用了另外一个便利构造器,而另外一个便利构造器以调用指定构造器结束
  21. }
  22. }

(三)关于构造器的继承与重载
swift中,子类不会默认继承父类的构造器。
构造器的重载遵循构造器链的规则(1.1-1.3)
构造器的继承规则如下:
2.1、如果子类中没有定义任何指定构造器,将会自动继承所有父类的指定构造器
2.2、如果子类中提供了所有父类指定构造器,不管是通过规则2.1继承来的,还是自定义实现的,它将继承所有父类的便利构造器。
注意:子类可以通过部分满足规则2.2的方式,使用子类便利构造器来实现父类的指定构造器。
例子一:

  1. class Base{
  2. var baseVar:String
  3. init(baseInput:String){
  4. baseVar = baseInput
  5. }
  6. convenience init(){
  7. self.init(baseInput:"basevar")
  8. }
  9. }
  10. class Sub:Base{
  11. var subVar:String = "subvar";
  12. }

这里子类没有定义任何构造器,所以满足规则2.1,2.1,将继承所有父类的指定构造器和便利构造器
所以可以这么调用

  1. var instance1 = Sub()
  2. var instance2 = Sub(baseInput:"newBaseVar")

例子二

  1. class Base{
  2. var baseVar:String
  3. init(baseInput:String){
  4. baseVar = baseInput
  5. }
  6. init(firstPart:String,secondPart:String){
  7. baseVar = firstPart + secondPart
  8. }
  9. convenience init(){
  10. self.init(baseInput:"basevar")
  11. }
  12. }
  13. class Sub:Base{
  14. var subVar:String;
  15. init(subInput:String,baseInput:String){
  16. subVar = subInput
  17. super.init(baseInput)
  18. }
  19. }

这里,子类只是实现了父类的一个构造器,所以并未继承便利构造器,也没有继承另外一个指定构造器
只可以这么创造实例

  1. var instance = Sub(subInput:"subvar",baseInput:"basevar")

(四)基于上述两个规则,构造过程分为两个部分
阶段一

  • 某个指定的构造器或者便利构造器被调用;
  • 完成新实例的内存分配(此时内存尚未初始化);
  • 指定构造器确保其引入的所有存储属性已经赋值(存储属性极其所属内存完成初始化);
  • 指定构造器调用父类构造器(父类构造器属性初始化);
  • 这个调用父类的构造器沿着构造器链一直向上,直到最顶部。(确保所有的继承的基类过程都已经初始化)。

阶段二

  • 从顶部一直向下,每个构造器链中类指定的构造器都有机会进一步定制实例,构造器此时可以访问self,修改它的属性并且调用实例方法等等。
  • 最终,任意构造器的便利构造器将有机会定制实例和使用self。

可能这个规则有点抽象,举个例子就明白了

  1. class Base{
  2. var baseVar:String
  3. init(baseInput:String){
  4. baseVar = baseInput
  5. }
  6. }
  7. class Sub:Base{
  8. var subVar:String;
  9. func subPrint(){
  10. println("现在可以调用实例方法了")
  11. }
  12. init(subInput:String,baseInput:String){
  13. subVar = subInput
  14. super.init(baseInput:baseInput)
  15. //这里就完成了阶段一
  16. self.subVar = subInput + "123"//此时可以调用self
  17. subPrint()//此时也可以调用实例方法了
  18. }
  19. }

总得来说:当类的实例的内存被初始化完成,也就是调用super.init()之后,就完成了阶段一了。

五、编译器的安全检查
检查一

指定构造器必须在它所在类的属性先初始化完成后才能把构造任务向上代理给父类中的构造器。简单来说,就是先初始化自己的存储属性,在调用父类的super.init向上初始化
检查二
指定构造器必须先向上调用父类构造器,在为继承来的属性赋初值。这个很简答,假设继承来个x,你先为x赋值为1了,而在调用父类构造器,父类构造器会为x赋另外一个初值来保证初始化过程完成,那么你赋值的1就被覆盖了
检查三
便利构造器先调用同类中其他构造器,再为任意属性赋初值。和检查二类似,也是防止被覆盖
检查四
构造器在第一阶段完成之前,不能饮用self,不能调用任何实例属性,不能调用实例方法

六、总结一下
指定构造器的过程是这样的
1、为自己的属性赋初值
2、调用基类构造器(super.init)
3、然后就可以调用self,和实例方法,存储属性。定制新的值了。
然后,我们看下官方文档里给出的一个比较好的例子

  1. class Food {
  2. var name: String
  3. init(name: String) {
  4. self.name = name
  5. }
  6. convenience init() {
  7. self.init(name: "[Unnamed]")
  8. }
  9. }
  10. class RecipeIngredient: Food {
  11. var quantity: Int
  12. init(name: String, quantity: Int) {
  13. self.quantity = quantity
  14. super.init(name: name)
  15. }
  16. override convenience init(name: String) {
  17. self.init(name: name, quantity: 1)
  18. }
  19. }
  20. class ShoppingListItem: RecipeIngredient {
  21. var purchased = false
  22. var description: String {
  23. var output = "(quantity) x (name.lowercaseString)" {
  24. output += purchased ? " YES" : " NO"
  25. return output
  26. }
  27. }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345