Class written in Swift
之前ofollow,noindex">TBUIAutoTest 有個issue ,我發現原因跟 Swift 有關,在解決問題時順帶稍微研究了下 Swift 編寫的類。
Swift Class 與 Ivar
純粹的 Swift 類(沒繼承自NSObject
)在 Runtime 上有很大的坑。雖然 Runtime 的介面都能呼叫,但因為Class
實現和構成有很大差異,所以需要謹慎對待。比如 Swift 沒有Ivar
的概念,相應的 Runtime 介面也只是儘可能的封裝,不保證返回的內容正確。Swift 將成員變數和屬性統一起來,並統一儲存和管理。
其實如果 Swift 類的屬性型別是繼承自NSObject
的話,還是可以通過Ivar
相關 Runtime 函式獲取到內容的。這也是TBUIAutoTest
能夠相容 Swift 的原因。有些 Objective-C 型別在 Swift 有對應的替代,比如NSString
與String
。編譯器會自動轉換介面和型別,但在這些型別上的屬性獲取 Ivar 依然有些問題。比如使用360" target="_blank" rel="nofollow,noindex">object_getIvar
就會BAD_ACCESS
。
Swift Class Runtime Name
在比較早的 Swift 版本,debug 時我們看到的 Swift 的類名都是一串很長很亂的字串,其實那是經過 Objective-C Runtime Mangle 後的產物。大概的規則如下:
- 字首是 “_Tt”
- 如果是 Class,還會再加一個 “C”,Protocol 會跟著一個 “P”
- Module名連著類名,並在每個名字前面標記字串長度。
如今 Swift 正醞釀著一套新的 Mangle 規則,但要等 Objective-C Runtime 那邊實現好新的 Demangle 後才能實施!有興趣可以看下mangleObjCRuntimeName 函式的實現。
現在 lldb 中列印 Swift 型別更加友好了,但是底層還是會生成一個 Runtime Name。在$(SWIFT_MODULE_NAME)-Swift.h
檔案中可以看到 Swift AST 對應 Objective-C 的標頭檔案,裡面就有 Swift Runtime Name。Swift 原始碼裡有個PrintAsObjC.cpp
檔案,它的作用就是生成 Swift AST 標頭檔案。
舉個栗子:使用NSClassFromString(@"_TtC19ClassWrittenInSwift11AppDelegate")
獲取到的類是ClassWrittenInSwift
Module 中的AppDelegate
類。
Swift Class Check
如何判斷一個類是否用 Swift 寫的呢?Runtime 中Class
是有標誌位的,只是沒對外暴露介面而已。對映到 Runtime 原始碼中Class
的記憶體模型,將標誌位取出即可,關鍵程式碼如下。
struct yxy_objc_object { yxy_isa_t isa; }; // class is a Swift class #define FAST_IS_SWIFT(1UL<<0) struct yxy_class_data_bits_t { // Values are the FAST_ flags above. uintptr_t bits; bool getBit(uintptr_t bit) { return bits & bit; } bool isSwift() { return getBit(FAST_IS_SWIFT); } }; struct yxy_objc_class : yxy_objc_object { // Class ISA; Class superclass; yxy_cache_t cache;// formerly cache pointer and vtable yxy_class_data_bits_t bits;// class_rw_t * plus custom rr/alloc flags }; BOOL isWrittenInSwift(Class cls) { if (!cls || !object_isClass(cls)) { return NO; } struct yxy_objc_class *objc_cls = (__bridge struct yxy_objc_class *)cls; bool isSwift = objc_cls->bits.isSwift(); return isSwift; }
實現很簡單,封裝了下:ClassWrittenInSwift
哎,自己的 Repo 真是越來越水了。
Swift Class Lazy Property
Swift 類的lazy
屬性的儲存比較特殊,畢竟是懶載入。它的屬性名有個字尾 “.storage”,所以在 Runtime 裡獲取屬性名時要注意,使用時是要過濾掉字尾的。
寫了個簡單的介面獲取 Swift 類中的lazy
屬性名列表,程式碼同樣放在ClassWrittenInSwift
裡面了。