//: Playground - noun: a place where people can play
import UIKit
// # define: 初始化是为类、结构体或者枚举准备实例的过程,目的是给实例里的每一个存储属性设置一个初始值并且执行其他配置.
// # 为存储属性设置初始化值
// 当你给一个存储属性分配默认值,或者在一个初始化器里设置它的初始值的时候,不会调用任何属性监听器。
// # 自定义初始化
// 可选类型的属性自动地初始化为 nil,相当于自带默认= nil.当然也可以对其进行初始化
class SurveyQuestion {
var text: String
var response: String? = "I don't think so..."
init(text: String) {
self.text = text
}
}
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
cheeseQuestion.response!
// 在初始化过程中,可以给常量属性赋值,一旦被赋值过一次,此后就不能再被赋值(不管在不在init中)
// # 默认初始化器
// 类:无自定义初始化器&&存储变量都有默认值&&是基类:自动获得:默认初始化器
// 结构体:无自定义初始化器:自动获得:成员初始化器;无自定义初始化器&&存储变量都有默认值:自动获得:成员初始化器和默认初始化器
// 如果想要自定义类型也能够使用默认初始化器或成员初始化器初始化,就自定义初始化器写在扩展里。***如果扩展的init()和默认初始化器同名怎办???
// # 值类型的初始化器委托
// 值类型初始化器可以调用其他初始化器来执行部分实例的初始化。使用self.init在一个init中调用同类型的其他init.类必须用convenience标记
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)
}
}
// # 类的继承和初始化
// 指定初始化器:主要初始化器,调用合适的父类初始化器来继续初始化过程.每个类至少要有一个指定初始化器(含默认初始化器)
// 便捷初始化器:在相同的类里定义一个便捷初始化器来调用一个指定的初始化器.在init关键字前加上convenience.作用是为指定初始化器传递一些参数(比如默认),这样更加方便.
class Prisoner {
var name: String
var age: Int
var anythingToSay: String
init(name: String, age: Int, anythingToSay: String = "") {
self.name = name
self.age = age
self.anythingToSay = anythingToSay
}
/*
init(name: String, age: Int) {
self.name = name
self.age = age
self.anythingToSay = "Nothing"
}*/
convenience init() {
// 优先调用后一个init,没有的时候才调用前者
self.init(name: "", age: 0)
}
}
// 类类型的初始化器委托规则***
// 1.指定初始化器必须从它的直系父类调用指定初始化器。向上委托
// 2.便捷初始化器必须从相同的类里调用另一个初始化器,并且最终必须在这个类中调用一个指定初始化器。横向委托
// 两段式初始化的安全检查规则***
// 1.指定初始化器必须保证在向上委托给父类初始化器之前,其所在类新引入的所有属性都要初始化完成。
// 2.指定初始化器必须先向上委托父类初始化器,然后才能为继承的属性设置新值。
// 3.便捷初始化器必须先委托同类中的其它初始化器,然后再为任意属性赋新值。
// 4.初始化器在第一阶段初始化完成之前,不能调用任何实例方法、不能读取任何实例属性的值,也不能引用 self 作为值(但是可用self.xxx为属性赋初值)或调用其他初始化器,执行完第一段,才算是有了这个类实例.
// 两段式初始化的过程***
// 调用便捷初始化器—(-调用其他便捷初始化器-)—调用同一类中的指定初始化器并初始化该类引入的属性—(—调用父类指定初始化器并初始化该类引入的属性—)-基类初始化完毕,第一阶段完成,实例诞生—修改初始化的内容或者调用self等进一步初始化—(—返回子类指定初始化器进一步初始化—)(-返回到便捷初始化器并修改实例的属性值—)—返回到调用的便捷初始化器并修改实例的属性值—-完成
// 初始化器的继承和重写***
// Swift子类默认不会继承父类的初始化器.如果父类指定初始化器与子类初始化器(指定或便捷)重名必须加上override修饰符.加上override的意义为:Swift可以数是否父类所有指定初始化器都被重写,如果是,将会把父类所有便捷初始化器赠送给子类.其他与自定义子类初始化器没有什么区别,比如都需要调用父类的指定初始化器(对指定初始化器)或子类的其他初始化器(对便捷初始化器),且在引入的属性初始化完成后.
// 基类.它的指定初始化器是默认初始化器
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
class Bicycle: Vehicle {
var hasBasket: Bool
override init() {
hasBasket = true // 引入的属性初始化
super.init() // 子类初始化器必须要调用父类的指定初始化器
numberOfWheels = 2 // 修改相关值,因为父类的初始化器不太好用
}
}
// 自动初始化器的继承规则***
// 1.如果子类没有定义任何指定初始化器而且引入的新属性都有默认值,它会自动继承父类所有的指定初始化器和便捷初始化器。???
// 2.如果子类override了所有父类指定初始化器,将自动继承父类所有便捷初始化器。可以将父类指定初始化器override为指定初始化器或便捷初始化器.往下看
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)
} // 将继承父类所有便捷初始化器。
/* OR:
override init(name: String) {
super.init(name: name)
}
*/
}
let oneMysteryItem = RecipeIngredient()
oneMysteryItem.name
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) \(name)"
output += purchased ? " Yes" : " No"
return output
}
}
var breakfastList = [
ShoppingListItem(),
ShoppingListItem(name: "Bacon"),
ShoppingListItem(name: "Eggs", quantity: 6),
] // trick: 长数组的创建格式
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true
for item in breakfastList {
print(item.description)
}
// # 可失败初始化器
// 不能定义可失败和非可失败的初始化器为相同的形式参数类型和名称。
// 严格来讲,初始化器不会有返回值。相反,它们的角色是确保在初始化结束时, self 能够被正确初始化。虽然写了 return nil 来触发初始化失败,但是不能使用 return 关键字来表示初始化成功了。
// trick: 类型转换
var a = Int(exactly: 3.14)
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
someCreature!.species
// 枚举的可失败初始化器***
enum MyTemperatureUnit {
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
// 带有原始值的枚举会自动获得一个可失败的初始化器init?(rawValue:)
enum TemperatureUnit: Character {
case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
}
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
let unknownUnit = TemperatureUnit(rawValue: "X")
// 类,结构体或枚举的可失败初始化器可以横向委托到同一个类,结构体或枚举里的另一个可失败初始化器。类似地,子类的可失败初始化器可以向上委托到父类的可失败初始化器。在任何位置都可能导致整个实例初始化失败而成为nil
// 可失败初始化器也可以委托其他的非可失败初始化器。通过这个方法,可以为已有的初始化过程添加初始化失败的条件。
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)
}
}
let twoSocks = CartItem(name: "sock", quantity: 2)
let zeroShirts = CartItem(name: "shirt", quantity: 0)
let oneUnnamed = CartItem(name: "", quantity: 1)
// 重写可失败初始化器
// 可以用一个非可失败初始化器重写一个可失败初始化器,但反过来是不行的。
// 下面的以下定义了一个名为 Document 的类。这个类建模了一个文档,其中的 name 属性要么是一个非空的字符串值要么为 nil ,但不能是一个空字符串:
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() // 其他的改变必须在super.init()之后,这就是两段式初始化
if name.isEmpty {
self.name = "[Untitled]"
} else {
self.name = name
}
}
}
class UntitledDocument: Document {
override init() {
super.init(name: "[Untitled]")!// 强制展开父类的可失败初始化器,因为这里肯定不会失败
}
}
// 隐式展开的可失败初始化器init!失败时会终止程序
// # 必要初始化器
// 在类的初始化器前添加 required 修饰符来表明所有该类的子类都必须实现该初始化器.
// 当子类重写父类的必要初始化器时,必须在子类的初始化器前同样添加 required 修饰符以确保当其它类继承该子类时,该初始化器同为必要初始化器。不需要添加 override 修饰符.
// 注意:如果子类继承的初始化器能够满足require需求,则无需显式地在子类中提供必要初始化器的实现。默认不继承,但是也可能继承便捷初始化器???
// # 通过闭包和函数来设置属性的默认值
// 实例的初始化过程中,闭包或函数提供存储属性的默认值
// trick:闭包花括号的结尾跟一个没有参数的圆括号。这是告诉 Swift 立即执行闭包。如果忽略了这对圆括号,你就会把闭包作为值赋给了属性,并且不会返回闭包的值。***
// 使用了闭包来初始化属性,在闭包执行的时候,实例的其他部分还没有被初始化,不能使用.
// 一个国际象棋棋盘建立的例子
// trick:二维数组用法
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 {
assert(row >= 0 || row <= 7 || column >= 0 || column <= 7, "Index out of range.") // 断言是不满足布尔条件时触发
return boardColors[(row * 8) + column]
}
}
let board = Chessboard()
print(board.squareIsBlackAt(row: 0, column: 1))
print(board.squareIsBlackAt(row: 7, column: 7))