构造(Initialization)与析构(Deinitialization)
- 存储属性必须有初值。可以在声明时赋缺省值,也可以在构造器赋初值。
存储属性为可选值时,其缺省值为nil。
赋初值时属性观测器不会被调用。
赋初值时可使用闭包或全局函数进行配置。
- 构造器(Initializers)语法形式为 init(外部名1 内部名1: 类型1, 外部名2 内部名2: 类型2 ...)
缺省形式为 init(外部名1兼内部名1: 类型1, 外部名2兼内部名2: 类型2 ...)
即构造器的所有参数缺省外部名与内部名相同,要省略外部名需要把外部名设置为下划线。
缺省形式 完整形式 最简形式 被调用方 class Person{
init(ea: Int, eb: Int){
ea ...
eb ...
}
}class Person{
init(ea la: Int, eb lb: Int){
la ...
lb ...
}
}class Person{
init(_ la: Int, _ lb: Int){
la ...
lb ...
}
}调用方 Person(ea: 1, eb: 2) Person(ea: 1, eb: 2) Person(1, 2) - 缺省构造器(Default Initializers):对于所有成员属性都具有缺省值的类和结构,编译器可以为其自动生成所有成员属性均被赋予缺省值的缺省构造器。
成员逐一构造器(Memberwise Initializers):对于结构,编译器可以为其自动生成所有成员属性均被逐一赋值的成员逐一构造器。
缺省构造器和成员逐一构造器的生成条件是类和结构没有用户自定义的构造器。 - 构造器委托(Initializer Delegation):构造器之间的相互调用被称为构造器委托。
- 类的构造器可分为两种。
指定构造器(Designated Initializers):处于首要地位的基本构造器。每个类必须具有至少一个指定构造器。指定构造器必须初始化类的所有成员。指定构造器必须向上委托,即必须调用直接基类的指定构造器。没有用户自定义的指定构造器的类将会继承基类的所有指定构造器。
便利构造器(Convenience Initializers):处于从属地位的辅助性构造器,通过关键字 convenience 指定。便利构造器必须横向委托,即必须调用所在类的其他构造器。便利构造器横向委托的终点必须是所在类的指定构造器。继承了或实现了基类的所有指定构造器的类将会继承基类的所有便利构造器。
- 构造过程有可能失败时,需要定义可失败构造器(Failable Initializers)。
可失败构造器使用关键字 init 加?或!。加?时构建可选值对象,加!时构建隐式拆包可选值对象。
可失败构造器使用语句 return nil 表示构造失败。
带原始值的枚举自带可失败构造器。 - 如果类的构造器必须被子类实现,需要定义必要构造器(Required Initializers)。
必要构造器使用 required 关键字。
- 重写基类的必要构造器使用 required 关键字。重写基类的其他构造器使用 override 关键字。
- 析构器使用 deinit 关键字。无法显式调用析构器。
// 构造器参数的外部名和内部名
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
// ..
}
init(kelvin: Double) {
// ..
}
init(_ celsius: Double) {
// ..
}
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
let freezingPointOfWater = Celsius(kelvin: 273.15)
let bodyTemperature = Celsius(37.0)
// 缺省构造器
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
// 成员逐一构造器
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
自动引用计数(ARC Automatic Reference Counting)
- 语言中没有垃圾回收器,采用自动引用计数来管理内存:对于同一个实例,创建时引用计数为0,多一次引用则引用计数自动加1,少一次引用则引用计数自动减1,引用计数为0时该实例自动被释放。
class Person {} let p1 = Person() // 引用计数为1 let p2 = p1 // 引用计数为2 p2 = nil // 引用计数为1 p1 = nil // 引用计数为0,实例被释放
- 引用计数会带来循环引用(cyclic references)问题:A实例内部包含一个指向B实例的引用,而B实例内部也包含一个指向A实例的引用。由于A实例和B实例引用计数都不为0,两者均无法释放。
- 为了解决循环引用问题,语言中引入弱引用(weak references)和非占有引用(unowned references),常规引用被称为强引用(strong references)。
强引用:参与引用计数的常规引用。
弱引用:不参与引用计数,生命周期中可指向空值的观察者引用。
非占有引用:不参与引用计数,生命周期中不应该指向空值的观察者引用。
所谓不参与引用计数,是说对于一个实例,即便多一次或者少一次弱引用或非占有引用,该实例的引用计数也不会发生变化,即弱引用或非占有引用不影响引用计数器对于实例的释放。强引用 弱引用 非占有引用 关键字 无 weak unowned 是否参与引用计数 是 否 否 let还是var let,var var let 是否optional 是(?!),否 是(?) 否 - 经典使用场景:
强引用 弱引用 非占有引用 人和房间问题 class Person {
var apartment: Apartment?
}class Apartment {
weak var tenant: Person?
}顾客和信用卡问题 class Customer {
var card: CreditCard?
}class CreditCard {
unowned let customer: Customer
}国家和首都问题 class Country {
var capitalCity: City!
}class City {
unowned let country: Country
}闭包类成员问题 class TempNotifier { var onChange: (Int) -> Void = {} var currentTemp = 72 init() { onChange = {[unowned self] newTemp in self.currentTemp = newTemp } } }
扩展(Extensions)
扩展可以为类型添加的功能如下:
- 添加计算属性(实例属性和类型属性)
extension Double { var km: Double { return self * 1_000.0 } var m: Double { return self } var cm: Double { return self / 100.0 } var mm: Double { return self / 1_000.0 } var ft: Double { return self / 3.28084 } } let oneInch = 25.4.mm print("One inch is \(oneInch) meters") // Prints "One inch is 0.0254 meters" let threeFeet = 3.ft print("Three feet is \(threeFeet) meters") // Prints "Three feet is 0.914399970739201 meters"
- 定义新的实例方法和类型方法(包括变动性方法)
extension Int { func repetitions(task: () -> Void) { for _ in 0..<self { task() } } } 3.repetitions { print("Goodbye!") } // Goodbye! // Goodbye! // Goodbye! // 变动性方法 extension Int { mutating func square() { self = self * self } } var someInt = 3 someInt.square() // someInt is now 9
- 定义新的构造器
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() } extension Rect { 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 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)
- 定义下标
extension Int { subscript(digitIndex: Int) -> Int { var decimalBase = 1 for _ in 0..<digitIndex { decimalBase *= 10 } return (self / decimalBase) % 10 } } 746381295[0] // 5 746381295[1] // 9 746381295[2] // 2 746381295[8] // 7
- 定义嵌套类型
extension Int { enum Kind { case Negative, Zero, Positive } var kind: Kind { switch self { case 0: return .Zero case let x where x > 0: return .Positive default: return .Negative } } } 3.kind // .Positive -27.kind // .Negative 0.kind // .Zero
- 使既有类型遵循某个协议
extension SomeType: SomeProtocol, AnotherProctocol { // ... }
协议(Protocols)
- 协议在其他语言中被称为接口。协议中包含一些遵循该协议的类型必须或者可选实现的要求。
术语 动作 状态 Swift 协议
protocol采纳某个协议
adopt a protocol遵循某个协议
conform to a protocolC#, Java 接口
interface实现某个接口
implement an interface已实现某个接口
implemented an interface - 协议中可包含的要求有:属性,方法(包括变动性方法),构造器(包括可失败构造器)。
- 协议可以作为类型来使用。存在协议类型的数组。
- 使既有类型遵循协议可以通过实现扩展来达成。协议本身也可以实现扩展。
泛型(Generics)
- 泛型函数(Generic Functions)
func swapTwoValues<T>(inout a: T, inout _ b: T) { let temp = a a = b b = temp }
- 泛型类型(Generic Types)
struct Stack<Element> { var items = [Element]() mutating func push(item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() } }
- 泛型约束(Type Constraints)
func findIndex<T: Equatable>(array: [T], _ valueToFind: T) -> Int? { for (index, value) in array.enumerate() { if value == valueToFind { return index } } return nil } let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3) // doubleIndex is an optional Int with no value, because 9.3 is not in the array let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea") // stringIndex is an optional Int containing a value of 2
- 关联类型(Associated Types)
protocol Container { associatedtype ItemType mutating func append(item: ItemType) var count: Int { get } subscript(i: Int) -> ItemType { get } } struct Stack: Container { // original Stack implementation var items = [Element]() mutating func push(item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() } // conformance to the Container protocol mutating func append(item: Element) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> Element { return items[i] } }
- Where子句(Where Clauses)
func allItemsMatch< C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable> (someContainer: C1, _ anotherContainer: C2) -> Bool { // check that both containers contain the same number of items if someContainer.count != anotherContainer.count { return false } // check each pair of items to see if they are equivalent for i in 0..<someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // all items match, so return true return true } var stackOfStrings = Stack() stackOfStrings.push("uno") stackOfStrings.push("dos") stackOfStrings.push("tres") var arrayOfStrings = ["uno", "dos", "tres"] if allItemsMatch(stackOfStrings, arrayOfStrings) { print("All items match.") } else { print("Not all items match.") } // Prints "All items match."