Initialization

  • swift中的initialization方法实现中不使用return不返回对象
    struct Fahrenheit {
        var temperature: Double
        init() {
            temperature = 32.0
        }
    }
    var f = Fahrenheit()
    print("The default temperature is \(f.temperature)° Fahrenheit")
    // Prints "The default temperature is 32.0° Fahrenheit"
    
  • 通过initializer创建初始值和在属性定义时赋予默认值,不会调用property observer
  • 由于init方法的方法名必须为init且不能重定义,所以括号内的argument label特别重要。并且如果你没有写,系统会为你提供。如果你不需要,也可以使用_去掉(和其他函数方法参数结构一样)
  • 类中的属性必须有default value,在属性定义和构造器中赋值,如果不赋予初始化值要标记成可选类型。不赋值的可选类型默认为空,可以被理解成还没有值,可以不在构造器中赋值。
  • 常量属性必须在定义时赋值,或者在构造器中赋值。对于类实例来说,只能在定义这个常量属性的类中使用构造器赋值,不能再子类的构造器中赋值。
    class SurveyQuestion {
        let text: String
        var response: String?
        init(text: String) {
            self.text = text
        }
        func ask() {
            print(text)
        }
    }
    let beetsQuestion = SurveyQuestion(text: "How about beets?")
    beetsQuestion.ask()
    // Prints "How about beets?"
    beetsQuestion.response = "I also like beets. (But not with cheese.)"
    
  • 当属性都有默认值时,且没有自定义构造器时,swift 会提供默认构造器。
    class ShoppingListItem {
        var name: String?
        var quantity = 1
        var purchased = false
    }
    var item = ShoppingListItem()
    
  • 对于结构体而言,当没有自定义构造器的时候,会自动生成逐一成员构造器。不同于上面的默认构造器,逐一成员构造器允许属性在定义的时候没有默认值。如果有自定义构造器,则不会生成默认构造器和逐一成员构造器,这是为了防止错用。如果想在自定义构造器的同时也生成默认,使用扩展(extension)
    struct Size {
        var width = 0.0, height = 0.0
    }
    let twoByTwo = Size(width: 2.0, height: 2.0)
    
  • 可以自定义多个构造器,在复杂构造器中使用self.init调用其他简单的自定义构造器
    struct Rect {
        var origin = Point()
        var size = Size()
        init() {}
        init(origin: Point, size: Size) {
            self.origin = origin
            self.size = size
        }
        init(center: Point, size: Size) {
            let originX = center.x - (size.width / 2)
            let originY = center.y - (size.height / 2)
            self.init(origin: Point(x: originX, y: originY), size: size)
        }
    }
    
    let basicRect = Rect()
    // basicRect's origin is (0.0, 0.0) and its size is (0.0, 0.0)
    
    let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
                        size: Size(width: 5.0, height: 5.0))
    // originRect's origin is (2.0, 2.0) and its size is (5.0, 5.0)
    
    let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
                        size: Size(width: 3.0, height: 3.0))
    // centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)
    
  • 指定构造器和普通构造器格式一样,至少要有一个;便利构造器在前面加上convenience关键字,可有可无。通常用必不可少的指定构造器来创建实例,而使用便利构造器来个性化定制子类。例如需要多个参数的指定构造器,可以创建需要一个参数的便利构造器,在实现中调用指定构造器,其他参数传默认值,做到传入更少参数便利创建实例,如下
     init(name: String) {
          self.name = name
      }
      convenience init() {
          self.init(name: "[Unnamed]]")
      }
    
  • 关于构造器原则:1.指定构造器必须调用其父类指定构造器;2.便利构造器必须调用类中其他构造器;3.便利构造器必须最终调用指定构造器。简单说:指定构造器向上代理,便利构造器横向代理
  • swift中构造器的两个阶段,第一个阶段赋予所有变量默认值,第二个阶段变量在使用前灵活处理
  • 构造器的安全检查:1.必须指定所有存储变量后才可以向上调用父类构造器;2.指定构造器必须在继承属性赋值前向上调用父类构造器,否则赋值会被覆盖;3.便利构造器必须先调用其他构造器,后赋值,原理同上;4.第一阶段结束前不允许调用和读取任何实例属性及实例方法
  • 第一阶段:类调用指定或便利构造器——为新实例分配内存,没有被完全初始化—— 指定构造器为所有存储属性赋初始值,所有存储属性拥有内存——指定构造器调用其父类构造器执行同样操作——反复调用继承树直到顶端——直到顶端所有属性存储属性有值之后,新实例内存完全分配完成,第一阶段结束
  • 第二阶段:从上往下,依次给每个指定构造器机会定制实例,更改属性并调用实例方法,直到低端
  • 指定构造器都在第一阶段,在便利构造器调用完指定构造器后可以修改变量,这部分是第二阶段,如下
    class Student {
        var name = "xiaoming"
        var old: String
        init() {
    //        print("\(self.name)")  第一阶段未结束,实例未初始化完成,读取self会导致error
            self.old = "16" //写入赋值可以 在指定构造器中设置默认值
        }
        convenience init(old: String) {
            self.init()
            //至此,第一阶段结束
            print("\(self.name)") //进入第二阶段,可以读取self,此后为灵活处理属性的地方
            //在便利构造器中定义个性值
            self.old = old
        }
    }
    
  • swift中子类不继承父类的构造方法
  • 关于构造器继承的两个规则
    1.如果子类没有定义任何指定构造器,则自动继承父类所有指定构造器
    2.如果子类实现了所有父类中的指定构造器,无论是自己定义还是由1继承的,那么子类会继承所有父类的便利构造器
  • 使用override重载父类构造方法,当子类存在和父类方法名相同的方法时必须要有Override,否则会报错
    class Bicyle: Vehicle {
        override init() {
            super.init()
            //至此第一阶段结束,子类有机会重写初始化值
            self.numberOfWheels = 2
        }
    }
    
  • 使用?表示可失败构造器,防止必要参数缺失时错误的成功创建实例
  • 严格说构造方法不返回实例,只是确保self在使用的时候被初始化完成,所以对于可失败构造器来说,失败返回nil,成功不用返回
    struct Animal {
        let species: String
        init?(species: String){
            if species.isEmpty { return nil }
            self.species = species
        }
    }
    
  • 枚举为原值自动提供可失败初始化方式
  • 使用可失败构造器调用自身或父类的另一个可失败构造器,可以使用新的可失败构造器在原可失败构造器上添加额外的失败情况
    class Product {
        let name: String
        init?(name: String) {
            if name.isEmpty{ return nil }
            self.name = name
        }
    }
    class cartItem: Product {
        let quantity: Int
        init?(name: String, quantity: Int) {
            if quantity < 1 { return nil }
            self.quantity = quantity
            super.init(name: name)
        }  
    }
    
  • 可使用不可失败构造器调用失败构造器,在其中给予失败条件固定值,并使用!对于确定不会失败的可失败构造器进行强制拆包
    class Document {
        var name: String?
        init() {
          
        }
        init?(name: String) {
            if name.isEmpty { return nil }
            self.name = name
        }
    }
    class AutomaticallyNamedDocument: Document {
        override init() {
            super.init()
            self.name = "[Untitled]"
        }
        override init?(name: String) {
            super.init()
            if name.isEmpty {
                self.name = "[Untitled]"
            } else {
                self.name = name
            }
        }
    }
    class UntitledDocument: Document {
        override init() {
            super.init(name: "[Untitled]")!
        }
    }
    
  • 使用required关键字来表示必备构造器,子类也必须实现,不需要override关键字而是继续使用required
  • 使用闭包给函数属性赋予初始值,注意闭包内类本身没有构造完成,所以不能在闭包函数内调用其他类属性(即使属性有默认值),不能在闭包内使用self,不能在闭包内调用其他类中函数。实际上就是给某些相对复杂的属性使用一个新的匿名函数赋值,而由于新的匿名函数没有参数,所以不能使用匿名函数外的变量。
    struct Chessboard {
      let boardColors: [Bool] = {
          var temporaryBoard = [Bool]()
          var isBlack = false
          for i in 1...8 {
              for j in 1...8 {
                  temporaryBoard.append(isBlack)
                  isBlack = !isBlack
              }
              isBlack = !isBlack
          }
          return temporaryBoard
      }()
      func squareIsBlckAt(row: Int, column: Int) -> Bool {
          return boardColors[(row * 8) + column]
      }
    }
    
代码示例

函数继承树关于构造器的继承以及指定构造器和便利构造器的应用。

class Food {
    var name: String
    init(name: String) {
        self.name = name
    }
    //提供一个带有默认值的便利构造器
    convenience init() {
        self.init(name: "[Unnamed]]")
    }
}

let namedMeat = Food.init(name: "Bacon")

class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) {
        //本类中的属性可以在super.init前面赋值,而父类的属性要在super.init后赋值
        self.quantity = quantity
        super.init(name: name)
    }
    //提供一个便利构造器不指定个数时默认为1
    //此方法实际上重载的是food中的指定构造器
    override convenience init(name: String) {
        self.init(name: name, quantity: 1)
    }
    //和父类中的便利构造器相同,但是不需要override重载
    convenience init() {
        self.init(name: "default", quantity: 0)
    }
}

let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient.init(name: "Bacon")
let sixEggs = RecipeIngredient.init(name: "Eggs", quantity: 6)

class AShoppingListItem: RecipeIngredient {
    var purchased = false
    // 使用闭包返回一个只读计算属性
    var description: String {
        var output = "\(quantity) x \(name) "
        output += purchased ? "✔️" : "❌"
        return output
    }
}

var breakfaseList = [AShoppingListItem(), AShoppingListItem.init(name: "Bacon"), AShoppingListItem.init(name: "Eggs", quantity: 6)]
breakfaseList[0].name = "Orange juice"
breakfaseList[0].purchased = true
for item in breakfaseList {
    print("\(item.description)")
}
补充几点
  • 方法名,参数个数,参数名完全相同的方法才算是一个方法,才需要用Override重载
  • 使用convenience定义的构造器一定要调用指定构造器,否则会报错
  • 如果子类中定义的convenience构造器有和父类中的某个指定构造器方法重叠,也需要使用override重载
  • 如果子类中定义的convenience构造器于父类中某个convenience方法相同,不需要使用override重载
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • 简介 *自定义构造过程 *默认构造器 *值类型的构造器代理 *类的继承和构造过程 *可失败构造器 *必需构造器 *...
    FishSha阅读 260评论 0 0
  • 20- 枚举,枚举原始值,枚举相关值,switch提取枚举关联值 Swift枚举: Swift中的枚举比OC中的枚...
    iOS_恒仔阅读 2,229评论 1 6
  •  构造过程是使用类、结构体或枚举类型一个实例的准备过程。在新实例可用前必须执行这个过程,具体操作包括设置实例中每个...
    EndEvent阅读 626评论 0 3
  • 123.继承 一个类可以从另外一个类继承方法,属性和其他特征。当一个类继承另外一个类时, 继承类叫子类, 被继承的...
    无沣阅读 1,372评论 2 4
  • 1.1-首先谈一下什么是CoreData? coreData是ios5之后苹果原生用于对象数据管理并且持久化(存储...
    wangwei_1916阅读 493评论 0 3