// 同于 Objective-C 的初始化器,Swift 初始化器不返回值
初始化器
//eg :
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"
默认的属性值
// 通过提供 temperature 属性的默认值,上述代码可以简化为
struct Fahrenheit {
var temperature = 32.0
}
自定义初始化
//eg
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0
形式参数名和实际参数标签
struct Color {
let red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}
// using success
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)
// using wrong
let veryGreen = Color(0.0, 1.0, 0.0)
// this reports a compile-time error - external names are required
无实际参数标签的初始化器形式参数
//写一个下划线( _ )替代明确的实际参数标签以重写默认行为。
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
init(_ celsius: Double) {
temperatureInCelsius = celsius
}
}
let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius is 37.0
可选属性类型
//一个名为 SurveyQuestion 的类,有一个可选 String 属性,名为 response
class SurveyQuestion {
var text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
cheeseQuestion.ask()
// prints "Do you like cheese?"
cheeseQuestion.response = "Yes, I do like cheese."
在初始化中分配常量属性(尽管现在 text 属性是一个常量,但是它依然可以在类的初始化器里设置)
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.)"
默认初始化器
//由于 ShoppingListItem 类的所有属性都有默认值,
//又由于它是一个没有父类的基类, ShoppingListItem 类自动地获得了一个默认的初始化器,使用默认值设置了它的所有属性然后创建了新的实例。
//( name 属性是一个可选 String 属性,所以它会自动设置为 nil 默认值,尽管这个值没有写在代码里。)
//上面的栗子给 ShoppingListItem 类使用默认初始化器以及初始化器语法创建新的实例,写作 ShoppingListItem() ,并且给这个新实例赋了一个名为 item 的变量。
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
结构体类型的成员初始化器
//结构体自动接收一个 init(width:heght:) 成员初始化器,你可以使用它来初始化一个新的 Size 实例:
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
值类型的初始化器委托(初始化器可以调用其他初始化器来执行部分实例的初始化。这个过程,就是所谓的初始化器委托,避免了多个初始化器里冗余代码)
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)
}
}
// using
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)
类的继承和初始化(指定初始化器,便利初始化器本质都是初始化器,区别只是委托方向)
// 所有类的存储属性——包括从它的父类继承的所有属性——都必须在初始化期间分配初始值。
// Swift 为类类型定义了两种初始化器以确保所有的存储属性接收一个初始值。这些就是所谓的指定初始化器和便捷初始化器。
//规则
1. 指定初始化器必须从它的直系父类调用指定初始化器。(super.xxx())
2. 便捷初始化器必须从相同的类里调用另一个初始化器。
3. 便捷初始化器最终必须调用一个指定初始化器。
总:
1. 指定初始化器必须总是向上委托。
2. 便利初始化器必须总是横向委托。
指定初始化器
// 语法
init(parameters) {
statements
}
便捷初始化器:横向依赖,最终调用一个指定初始器
// 语法
convenience init(parameters) {
statements
}
两段式初始化
Swift 的类初始化是一个两端式过程。在第一个阶段,每一个存储属性被引入类为分配了一个初始值。一旦每个存储属性的初始状态被确定,第二个阶段就开始了,每个类都有机会在新的实例准备使用之前来定制它的存储属性。
两段式初始化过程的使用让初始化更加安全,同时在每个类的层级结构给与了完备的灵活性。两段式初始化过程可以防止属性值在初始化之前被访问,还可以防止属性值被另一个初始化器意外地赋予不同的值。
初始化器的继承和重写(不像在 Objective-C 中的子类,Swift 的子类不会默认继承父类的初始化器。Swift 的这种机制防止父类的简单初始化器被一个更专用的子类继承并被用来创建一个没有完全或错误初始化的新实例的情况发生。)
// 即使是自动提供的默认初始化器你也可以重写。你必须写 override 修饰符
// 当重写父类指定初始化器时,你必须写 override 修饰符,就算你子类初始化器的实现是一个便捷初始化器。
// 但是,如同上边类类型的初始化器委托所描述的规则那样,如果你写了一个匹配父类便捷初始化器的子类初始化器,父类的便捷初始化器将永远不会通过你的子类直接调用。因此,你的子类不能(严格来讲)提供父类初始化器的重写。当提供一个匹配的父类便利初始化器的实现时,你不用写 override 修饰符
//父
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
let vehicle = Vehicle()
print("Vehicle: \(vehicle。description)")
// Vehicle: 0 wheel(s)
//子(子类可以在初始化时修改继承的变量属性,但是不能修改继承过来的常量属性。)
class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
let bicycle = Bicycle()
print("Bicycle: \(bicycle.description)")
// Bicycle: 2 wheel(s)
自动初始化器的继承
// 子类默认不会继承父类初始化器
// 特定的情况下父类初始化器是可以被自动继承的
特定情况:(就算你的子类添加了更多的便捷初始化器,下面规则仍然适用。)
1. 你的子类没有定义任何指定初始化器,它会自动继承父类所有的指定初始化器。
2. 如果你的子类提供了所有父类指定初始化器的实现——要么是通过规则1继承来的,要么通过在定义中提供自定义实现的——那么它自动继承所有的父类便捷初始化器。
指定和便利初始化器的操作
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon"
let mysteryMeat = Food()
// mysteryMeat's name is "[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)
}
}
// RecipeIngredient 类提供的 init(name: String) 便捷初始化器接收与 Food 中指定初始化器 init(name: String) 相同的形式参数。
// 因为这个便捷初始化器从它的父类重写了一个指定初始化器,它必须使用 override 修饰符(如同在初始化器的继承和重写中描述的那样)。
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)
}
// 1 x Orange juice ✔
// 1 x Bacon ✘
// 6 x Eggs ✘
可失败初始化器(通过在 init 关键字后面添加问号( init? )来写。
let wholeNumber: Double = 12345.0
let pi = 3.14159
if let valueMaintained = Int(exactly: wholeNumber) {
print("\(wholeNumber) conversion to int maintains value")
}
// Prints "12345.0 conversion to int maintains value"
let valueChanged = Int(exactly: pi)
// valueChanged is of type Int?, not Int
if valueChanged == nil {
print("\(pi) conversion to int does not maintain value")
}
// Prints "3.14159 conversion to int does not maintain value"
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?, not Animal
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
// prints "An animal was initialized with a species of Giraffe"
let anonymousCreature = Animal(species: "")
// anonymousCreature is of type Animal?, not Animal
if anonymousCreature == nil {
print("The anonymous creature could not be initialized")
}
// prints "The anonymous creature could not be initialized"
枚举的可失败初始化器
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("This is a defined temperature unit, so initialization succeeded.")
}
// prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// prints "This is not a defined temperature unit, so initialization failed."
带有原始值枚举的可失败初始化器
enum TemperatureUnit: Character {
case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
}
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// prints "This is not a defined temperature unit, so initialization failed."
初始化失败的传递
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("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
// prints "Item: sock, quantity: 2"
// 如果你创建了一个 CartItem 实例, quantity 的值为 0 , CartItem 初始化器会导致初始化失败:
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
print("Unable to initialize zero shirts")
}
// prints "Unable to initialize zero shirts"
// 类似地,如果你尝试创建一个 CartItem 实例,并且令 name 为空值,那么父类 Product 的初始化器就会导致初始化失败
if let oneUnnamed = CartItem(name: "", quantity: 1) {
print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
print("Unable to initialize one unnamed product")
}
// prints "Unable to initialize one unnamed product"
重写可失败初始化器
class Document {
var name: String?
// this initializer creates a document with a nil name value
init() {}
// this initializer creates a document with a non-empty name value
init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}
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]")!
}
}
可失败初始化器 init!(init! 初始化器导致初始化失败时会触发断言。)
通常来讲我们通过在 init 关键字后添加问号 ( init? )的方式来定义一个可失败初始化器以创建一个合适类型的可选项实例。另外,你也可以使用可失败初始化器创建一个隐式展开具有合适类型的可选项实例。通过在 init 后面添加惊叹号( init! )是不是问号。
你可以在 init? 初始化器中委托调用 init! 初始化器,反之亦然。 你也可以用 init! 重写 init? ,反之亦然。 你还可以用 init 委托调用 init! ,尽管当 init! 初始化器导致初始化失败时会触发断言。
必要初始化器 (required)
class SomeClass {
required init() {
// initializer implementation goes here
}
}
//当子类重写父类的必要初始化器时,必须在子类的初始化器前同样添加 required 修饰符以确保当其它类继承该子类时,该初始化器同为必要初始化器。在重写父类的必要初始化器时,不需要添加 override 修饰符:
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
通过闭包和函数来设置属性的默认值
// 注意闭包花括号的结尾跟一个没有参数的圆括号。
// 这是告诉 Swift 立即执行闭包。
// 如果你忽略了这对圆括号,你就会把闭包作为值赋给了属性,并且不会返回闭包的值。
class SomeClass {
let someProperty: SomeType = {
// create a default value for someProperty inside this closure
// someValue must be of the same type as SomeType
return someValue
}()
}
//eg
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 squareIsBlackAt(row: Int, column: Int) -> Bool {
return boardColors[(row * 8) + column]
}
}
let board = Chessboard()
print(board.squareIsBlackAt(row: 0, column: 1))
// Prints "true"
print(board.squareIsBlackAt(row: 7, column: 7))
// Prints "false"