站在彙編角度深入瞭解 Swift(十一)

小星星_ios發表於2020-04-05

可選鏈(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()
複製程式碼

思考

  1. type(of: p) == Person.self? type(of: p) 和 sizeof 這種是一樣的,本質不是呼叫函式,只是將 p 的前8個位元組取出來,所以 type(of: p)和 Person.self 是相等的

  2. 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,不然用類名一般就夠用了

相關文章