Swift 中的 Runtime
即使在 Swift APP 中沒有一行 Object-c 的程式碼,每個 APP 也都會在 Object-c runtime 中執行,為動態任務分發和執行時物件關聯開啟了一個世界。更確切地說,可能在僅使用 Swift 庫的時候只執行 Swift runtime。但是使用 Objective-C runtime 這麼長時間,我們也應該讓他充分發揮其作用。
下面我們將以 Swift 的視角來觀察關聯物件(associated objects])和方法交叉(method swizzling) 這兩個在執行時的技術。
關聯物件(Associated Objects)
Swift extension 可以給已經存在 Cocoa 類新增極為豐富的功能,具體有: (1)新增計算例項屬性 ( computed property) 和計算類屬性
(2)定義例項方法和類方法
(3)提供新的構造器
(4)定義下標(subscript)
(5)定義和使用新的巢狀型別
(6)使一個遵守某個介面
相比之下, Objective-C 的 category 就遜色多了。比如說 Objective-C 中的 extension 就無法向既有類新增屬性。
慶幸的是 Objective-C 的 關聯物件(Associated Objects) 可以改善這個缺憾。例如要向一個工程裡所有的 view controllers 中新增一個 descriptiveName 屬性,我們可以簡單的使用 objc_get/setAssociatedObject()
來填充其 get 和 set 塊:
```
Swift
extension UIViewController { private struct AssociatedKeys { static var DescriptiveName = "nsh_DescriptiveName" }
var descriptiveName: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.DescriptiveName,
newValue as NSString?,
UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC)
)
}
}
}
``` 注意,在私有巢狀 struct 中使用 static var,這樣會生成我們所需的關聯物件鍵,但不會汙染整個名稱空間。
方法交叉(Method Swizzling)
有時為了方便,也有可能是解決某些框架內的 bug,或者別無他法時,需要修改一個已經存在類的方法的行為。方法交叉可以實現兩個方法的交換,相當於是用你自己寫的方法來過載原有方法,並且還能夠是原有方法的行為保持不變。
下面,我們說一個例子,在這個例子中我們交叉 UIViewController 的 viewWillAppear 方法,然後列印出每一個在螢幕上顯示的 view。方法交叉發生在 initialize 類方法呼叫時(如下程式碼所示);替代的實現在 nsh_viewWillAppear 方法中: ``` Swift extension UIViewController { public override class func initialize() { struct Static { static var token: dispatch_once_t = 0 }
// make sure this isn't a subclass
if self !== UIViewController.self {
return
}
dispatch_once(&Static.token) {
let originalSelector = Selector("viewWillAppear:")
let swizzledSelector = Selector("nsh_viewWillAppear:")
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
}
// MARK: - Method Swizzling
func nsh_viewWillAppear(animated: Bool) {
self.nsh_viewWillAppear(animated)
if let name = self.descriptiveName {
println("viewWillAppear: \(name)")
} else {
println("viewWillAppear: \(self)")
}
}
} ``` load vs. initialize (Swift 版本)
Objective-C runtime 理論上會在載入和初始化類的時候呼叫兩個類方法: load and initialize。在講解 method swizzling 的原文中曾指出出於安全性和一致性的考慮,方法交叉過程 永遠 會在 load() 方法中進行。每一個類在載入時只會呼叫一次 load 方法。另一方面,一個 initialize 方法可以被一個類和它所有的子類呼叫,比如說 UIViewController 的該方法,如果那個類沒有被傳遞資訊,那麼它的 initialize 方法就永遠不會被呼叫了。
可不同的是,在 Swift 中 load 類方法是不會被 runtime 呼叫,因此 Method Swizzling 就沒有辦法來實現,但是,我們有如下兩個方法可以來解決:
1.在 initialize 中實現方法交叉 這種做法很安全,你只需要確保相關的方法交叉在一個 dispatch_once 中就好了(這也是最推薦的做法)。
2.在 app delegate 中實現方法交叉 不像上面通過類擴充套件進行方法交叉,而是簡單地在 app delegate 的 application(_:didFinishLaunchingWithOptions:) 方法呼叫時中執行相關程式碼也是可以的。基於對類的修改,這種方法應該就足夠確保這些程式碼會被執行到。
最後,提醒大家,在不得已的情況下才去使用 Objective-C runtime。隨便修改基礎框架或所使用的三方程式碼會給專案造成很大的影響。請務必要小心哦。
備註:本文已經得到原作者的同意,授權 OneAPM 技術部落格進行轉載
OneAPM Mobile Insight 以真實使用者體驗為度量標準進行 Crash 分析,監控網路請求及網路錯誤,提升使用者留存。訪問 OneAPM 官方網站感受更多應用效能優化體驗,想閱讀更多技術文章,請訪問 OneAPM 官方技術部落格。
相關文章
- runtime UIButton+badge (Swift 4.2)UISwift
- swift 中的 ??Swift
- Swift 中的 @autoclosureSwift
- Swift中的反射Swift反射
- swift 中的 lazySwift
- swift 中的 whereSwift
- Runtime中的 isa 結構體結構體
- Swift 4 中的字串Swift字串
- Swift中的模式匹配Swift模式
- Swift 中的屬性Swift
- Swift中的協議Swift協議
- swift 中的 @UIApplicationMainSwiftUIAPPAI
- swift 中的 SelectorSwift
- Swift中的SelectorSwift
- Runtime在工作中的運用
- 詳解 Objective-C 中的 RuntimeObject
- ios中Runtime的介紹以及使用iOS
- Swift 4 中的泛型Swift泛型
- Swift 中的訪問控制Swift
- swift 中的 AnyObject 和 AnySwiftObject
- Swift3中的 GCDSwiftGC
- Swift中的奇淫巧技Swift
- Swift中的RactiveCocoa (上)Swift
- swift中 Selector的使用Swift
- Swift 中的指標使用Swift指標
- 行走於 Swift 的世界中Swift
- Swift 中的泛型使用Swift泛型
- Swift中的Weak/Strong DanceSwift
- Swift中的ARC相關Swift
- iOS面試中經常問的點 – RunTimeiOS面試
- iOS面試中經常問的點 - RunTimeiOS面試
- Spark RDD中Runtime流程解析Spark
- Swift中URL處理中的注意點Swift
- iOS Runtime(一) Runtime的應用iOS
- KeyPath在Swift中的妙用Swift
- [譯]Swift 中的型別擦除Swift型別
- [譯] Swift 模組中的 API 汙染SwiftAPI
- [譯] Swift 中的動態特性Swift