可選鏈(Optional Channing)
- 如果可選項為 nil,呼叫方法、下標、屬性失敗,結果為 nil
- 如果可選項不為 nil,呼叫方法、下標、屬性成功,結果會被包裝成可選項
- 如果結果本來就是可選項,不會進行再次包裝
協議
- 協議中定義屬性時必須用 var 關鍵字
- 因為你有可能是用計算屬性實現的,那麼這個值就不確定了,所以不能使用 let 關鍵字
- 實現協議時的屬性許可權要不小於協議中定義的屬性許可權
- 協議定義 { get set },用 var 儲存屬性或 get、set計算屬性去實現
- 協議定義 get,用任何屬性都可以實現
- 為了保證通用,協議中必須用 static 定義型別方法、型別屬性,型別下標
mutating
- 只有將協議中的例項方法標記為 mutating
- 才允許結構體、列舉的具體實現修改自身記憶體
- 類在實現方法時不用加 mutating,列舉、結構體才需要加 mutating
init
- 協議中還可以定義初始化器 init
- 非 final 類實現時必須加上 required
- 如果協議實現的初始化器,剛好是重寫了父類的指定初始化器
- 那麼這個初始化器必須同時加 required、override
- init、init?、init!
- 協議中定義的 init?、init!,可以用 init、init?、init! 去實現
- 協議中定義的 init,可以用 init、init! 去實現
protocol Drawable {
func draw()
var x: Int { get set }
var y: Int { get }
subscript(index: Int) -> Int { get }
}
class Person: Drawable {
var x: Int = 0
let y: Int = 10
func draw() {
print("Person draw")
}
subscript(index: Int) -> Int {
return index
}
}
複製程式碼
協議組合
- 可以包含很多個協議,可以包含一個類型別(最多一個)
protocol P1 {
}
protocol P2 {
}
class Person { }
func fn0(obj: P1 & P2 & Person) { }
複製程式碼
CaseIterable
- 讓列舉遵守 CaseIterable 就可以遍歷列舉
enum Season {
case spring, summer, autumn, winter
}
extension Season: CaseIterable { }
func test34() {
let seasons = Season.allCases
print(seasons)
}
複製程式碼
CustomStringConvertible
- 遵守 CustomStringConvertible 協議,可以自定義例項的列印字串
- 和 OC 中的 description一樣
Any、AnyObject
- Swift 提供了2種特殊的型別
- Any:可以代表任意型別(列舉、結構體、類,也包含函式型別)
- AnyObject:可以代表任意類型別(在協議後面寫上: AnyObject 代表只有類能遵守這個協議),在後面加 class 關鍵字也有這個作用
is、as?、as!、as
- is 用來判斷是否為某種型別,as用來強制型別轉換
- as? 表示可能轉換失敗, as! 表示對 as? 的結果進行解包
(stu as? Student)?.study() // 轉化有可能會失敗,失敗的時候方法不呼叫
(stu as? Student)!.study() // 轉化有可能會失敗,但也強制解包,所以有可能發生閃退
(stu as! Student).study() // 和上面的完全等價,可以看成是上面這種寫的簡寫
複製程式碼
as 在編譯器層面就能確保你轉換是一定能夠成功的了
var d = 10 as Double
複製程式碼
X.self、X.Type、AnyClass
- X.self 是一個元型別(metadata)指標,metadata 存放著型別相關資訊。
- 也就是堆記憶體的首8個位元組
- X.self 屬於 X.Type 型別
- public typealias AnyClass = AnyObject.Type
class Person { }
class Student: Person { }
let person = Person()
var perType: Person.Type = Person.self
let stuType: Student.Type = Student.self
perType = Student.self
var anyType: AnyObject.Type = Person.self
anyType = Student.self
// public typealias AnyClass = AnyObject.Type
var anyType2: AnyClass = Person.self
anyType2 = Student.self
---------------------彙編分析---------------------
register read rax
rax = 0x0000000100014e48 type metadata for Person #1 in swiftstudy.test36() -> ()
(lldb) register read rax
rax = 0x0000000102900000
(lldb) x/5wg 0x0000000102900000
0x102900000: 0x0000000100014e48 0x0000000000000002
0x102900010: 0x0000000000000000 0x0000000000000000
0x102900020: 0x2f6d657473790017
複製程式碼
從上面的彙編也驗證了,其實X.self就是一個元型別(metadata)指標
元型別的使用
class Person {
var age: Int = 0
}
class Student: Person {
var no: Int = 0
}
print(class_getInstanceSize(Person.self))
print(class_getSuperclass(Student.self)!)
print(class_getSuperclass(Person.self)!)
---------------------彙編分析---------------------
24
Person
_TtCs12_SwiftObject
複製程式碼
可以看出來, Swift 還有個隱藏的基類:Swift.__SwiftObject,其實他和 oc 一樣,前8個位元組存的也是 isa 指標(就是用於查詢他的類資訊的),其實也可以理解,列印所有的純 swift 物件,可以發現一個規律,就是前8個位元組儲存 isa,後面跟著8個位元組儲存引用計數,把他們抽成一個父類其實更合理。
Self
- Self 一般用作返回值型別,限定返回值方法呼叫者必須是同一型別(也可以作為引數型別)
- 如果 Self 用在類中,要求返回時呼叫的初始化是 required 的
- 一般如果我想鏈式呼叫的話,會這樣寫
class Person {
required init() {
}
func test() -> Self {
Self.self.init()
}
func test1() -> Self {
Self.self.init()
}
}
class Student: Person {
}
let p = Person().test().test1()
複製程式碼
思考
-
type(of: p) == Person.self? type(of: p) 和 sizeof 這種是一樣的,本質不是呼叫函式,只是將 p 的前8個位元組取出來,所以 type(of: p)和 Person.self 是相等的
-
Person 和 Person.self 的異同
class Person {
static var age = 0
static func run() {}
}
Person.age = 10
Person.self.age = 20
func test(_ cls: AnyClass) {
}
test(Person.self)
複製程式碼
也就是說當你明確需要傳 X.Type 的時候,這個時候X.self,不然用類名一般就夠用了