1. 存储属性的初始赋值
1.1 构造器
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)")
// print "The default temperature is 32.0"
上面这个结构体定义了一个不带参数的构造器init
,并将存储属性temperature
值初始化为32.0
。
1.2 默认属性值
struct Fahrenheit {
var temperature = 32.0
}
使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型。
2 自定义构造过程
2.1 构造参数
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}
第一个构造器拥有一个构造参数,外部参数名为fromFahrenheit
,内部参数名为fahrenheit
。
2.2 不带外部名的构造器参数
我们可以用_
来代替外部参数名。
2.3 可选属性类型
class SurveyQuestion {
var text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
3. 默认构造器
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
尽管代码中没有显式地为name
属性设置默认值,但由于name
是可选字符串类型,它将默认值设置为nil
。
3.1 结构体的逐一成员构造器
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
4. 值类型的构造器代理
构造器可以通过调用其它构造器来完成实例部分构造过程。这一过程成为构造器代理,它能减少多个构造器间的代码重复。
如果你为某个值类型定义了一个自定义的构造器,你将无法访问到默认构造器(如果是结构体,还将无法访问逐 一成员构造器)。这种限制可以防止你为值类型增加了一个额外的且十分复杂的构造器之后,仍然有人错误的使用 自动生成的构造器。
假如你希望默认构造器、逐一成员构造器以及你自己的自定义构造器都能用来创建实例,可以将自定义的构造器 写到扩展(extension
)中,而不是写在值类型的原始定义中。
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
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)
}
}
5. 类的继承和构造过程
类里面的所有存储型属性——包括所有继承自父类的属性——都必须在构造过程中设置初始值。
Swift 为类类型提供了两种构造器来确保实例中所有存储型属性都能获得初始值,它们分别是指定构造器和便利 构造器。
5.1 指定构造器和便利构造器的语法
指定构造器:
init(parameters) {
statements
}
便利构造器:
convenience init(parameters) {
statements
}
5.1 类的构造器代理规则
规则 1
指定构造器必须调用其直接父类的的指定构造器。
规则 2
便利构造器必须调用同类中定义的其它构造器。
规则 3
便利构造器必须最终导致一个指定构造器被调用。
一个更方便的记忆方法是:指定构造器必须总是向上代理,便利构造器必须总是横向代理。
5.2 构造器的继承和重写
跟 Objective-C 中的子类不同,Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止 一个父类的简单构造器被一个更精细的子类继承,并被错误地用来创建子类的实例。
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
子类Bicycle
定义了一个指定构造器init()
。调用super.init()
方法,可以确保Bicycle
在修改属性之前,它所继承的属性numberOfWheels
能被Vehicle
类初始化。
注意
子类可以在初始化时修改继承来的变量属性,但是不能修改继承来的常量属性。
5.3 构造器的自动继承
假设你为子类中引入的所有新属性都提供了默认值,以下 2 个规则适用:
规则 1
如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
规则 2
如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将 自动继承所有父类的便利构造器。
5.4 指定构造器和便利构造器实践
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? "√" : "×"
return output
}
}
var breakfastList = [
ShoppingListItem(),
ShoppingListItem(name: "Bacon"),
ShoppingListItem(name: "Eggs", quantity: 6)
]
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true
for item in breakfastList {
print(item.description)
}
// print
//"1 x Orange juice √
//1 x Bacon ×
//6 x Eggs ×
//"
6. 可失败构造器
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty {
return nil
}
self.species = species
}
}
let someCreature = Animal(species: "Giraffe")
// someCreature 的类型是 Animal? 而不是 Animal
if let giraffe = someCreature {
print("\(giraffe.species)")
}
// print "Giraffe"
let anonymousCreature = Animal(species: "")
if anonymousCreature == nil {
print("not initialized")
}
// print "not initialized"
6.1 枚举类型的可失败构造器
你可以通过带一个或多个参数的可失败构造器来获取枚举类型中特定的枚举成员,如果提供的参数无法匹配任何枚举成员,则构造失败。
enum TemperatureUnit {
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("initialization succeeded")
}
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("initialization failed")
}
// print "initialization failed"
6.2 带原始值的枚举类型的可失败构造器
带原始值的枚举类型会自带一个可失败构造器init?(rawValue:)
,该可失败构造器有一个名为rawValue
的参数,如果该参数的值能够和某个枚举成员的原始值匹配,则该构造器会构造相应的枚举成员,否则构造失败。
因此上面的例子可以重写为:
enum TemperatureUnit: Character {
case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
}
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
print("initialization succeeded.")
}
// print "initialization succeeded."
let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
print("initialization failed.")
}
// print "initialization failed."
6.3 构造失败的传递
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)
}
}
if let twoSocks = CartItem(name: "sock", quantity: 2) {
print("initialization succeeded")
}
// print "initialization succeeded"
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
print("initialization succeeded")
} else {
print("Unable to initialize")
}
// print "Unable to initialize"
if let oneUnnamed = CartItem(name: "", quantity: 1) {
print("initialization succeeded")
} else {
print("Unable to initialize")
}
// print "Unable to initialize"
6.4 重写一个可失败构造器
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]")
}
}
6.5 可失败构造器init!
通常来说我们通过在init关键字后添加问号的方式(init?
)来定义一个可失败构造器,但你也可以通过在init
后面添加惊叹号的方式来定义一个可失败构造器(init!
),该可失败构造器将会构建一个对应类型的隐式解包可选类型的对象。
一旦init!
构造失败,则会触发一个断言。
7. 必要构造器
在类的构造器前添加required
修饰符表明所有该类的子类都必须实现该构造器:
class SomeClass {
required init() {
// 构造器的实现代码
}
}
在子类重写父类的必要构造器时,必须在子类的构造器前也添加required
修饰符,表明该构造器要求也应用于继承链后面的子类。在重写父类中必要的指定构造器时,不需要添加override
修饰符。
class SomeSubclass: SomeClass {
required init() {
// 构造器的实现代码
}
}
8. 通过闭包或函数设置属性的默认值
class SomeClass {
let someProperty: SomeType = {
// 在这个闭包中给 someProperty 创建一个默认值
// someValue 必须和 SomeType 类型相同
return someValue
}()
}
闭包结尾的小括号告诉 Swift 立即执行次闭包,如果忽略了括号,相当于将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。
注意:
如果使用闭包来初始化属性,闭包在执行时,实例的其它部分还没有初始化。这意味着你不能在闭包里访问其它属性,即使这些属性有默认值。同样,你也不能使用隐式的self
属性,或者调用任何实例方法。