- 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重载