摘要:更確切地說(shuō),可能在僅使用庫(kù)的時(shí)候只運(yùn)行。比如說(shuō)中的就無(wú)法向既有類(lèi)添加屬性。在講解的原文中曾指出出于安全性和一致性的考慮,方法交叉過(guò)程永遠(yuǎn)會(huì)在方法中進(jìn)行。隨便修改基礎(chǔ)框架或所使用的三方代碼會(huì)給項(xiàng)目造成很大的影響。
即使在 Swift APP 中沒(méi)有一行 Object-c 的代碼,每個(gè) APP 也都會(huì)在 Object-c runtime 中運(yùn)行,為動(dòng)態(tài)任務(wù)分發(fā)和運(yùn)行時(shí)對(duì)象關(guān)聯(lián)開(kāi)啟了一個(gè)世界。更確切地說(shuō),可能在僅使用 Swift 庫(kù)的時(shí)候只運(yùn)行 Swift runtime。但是使用 Objective-C runtime 這么長(zhǎng)時(shí)間,我們也應(yīng)該讓他充分發(fā)揮其作用。
下面我們將以 Swift 的視角來(lái)觀察關(guān)聯(lián)對(duì)象(associated objects])和方法交叉(method swizzling) 這兩個(gè)在運(yùn)行時(shí)的技術(shù)。
關(guān)聯(lián)對(duì)象(Associated Objects)Swift extension 可以給已經(jīng)存在 Cocoa 類(lèi)添加極為豐富的功能,具體有:
(1)添加計(jì)算實(shí)例屬性 ( computed property) 和計(jì)算類(lèi)屬性
(2)定義實(shí)例方法和類(lèi)方法
(3)提供新的構(gòu)造器
(4)定義下標(biāo)(subscript)
(5)定義和使用新的嵌套類(lèi)型
(6)使一個(gè)遵守某個(gè)接口
相比之下, Objective-C 的 category 就遜色多了。比如說(shuō) Objective-C 中的 extension 就無(wú)法向既有類(lèi)添加屬性。
慶幸的是 Objective-C 的 關(guān)聯(lián)對(duì)象(Associated Objects) 可以改善這個(gè)缺憾。例如要向一個(gè)工程里所有的 view controllers 中添加一個(gè) descriptiveName 屬性,我們可以簡(jiǎn)單的使用 objc_get/setAssociatedObject()來(lái)填充其 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,這樣會(huì)生成我們所需的關(guān)聯(lián)對(duì)象鍵,但不會(huì)污染整個(gè)命名空間。
有時(shí)為了方便,也有可能是解決某些框架內(nèi)的 bug,或者別無(wú)他法時(shí),需要修改一個(gè)已經(jīng)存在類(lèi)的方法的行為。方法交叉可以實(shí)現(xiàn)兩個(gè)方法的交換,相當(dāng)于是用你自己寫(xiě)的方法來(lái)重載原有方法,并且還能夠是原有方法的行為保持不變。
下面,我們說(shuō)一個(gè)例子,在這個(gè)例子中我們交叉 UIViewController 的 viewWillAppear 方法,然后打印出每一個(gè)在屏幕上顯示的 view。方法交叉發(fā)生在 initialize 類(lèi)方法調(diào)用時(shí)(如下代碼所示);替代的實(shí)現(xiàn)在 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 理論上會(huì)在加載和初始化類(lèi)的時(shí)候調(diào)用兩個(gè)類(lèi)方法: load and initialize。在講解 method swizzling 的原文中曾指出出于安全性和一致性的考慮,方法交叉過(guò)程 永遠(yuǎn) 會(huì)在 load() 方法中進(jìn)行。每一個(gè)類(lèi)在加載時(shí)只會(huì)調(diào)用一次 load 方法。另一方面,一個(gè) initialize 方法可以被一個(gè)類(lèi)和它所有的子類(lèi)調(diào)用,比如說(shuō) UIViewController 的該方法,如果那個(gè)類(lèi)沒(méi)有被傳遞信息,那么它的 initialize 方法就永遠(yuǎn)不會(huì)被調(diào)用了。
可不同的是,在 Swift 中 load 類(lèi)方法是不會(huì)被 runtime 調(diào)用,因此 Method Swizzling 就沒(méi)有辦法來(lái)實(shí)現(xiàn),但是,我們有如下兩個(gè)方法可以來(lái)解決:
1.在 initialize 中實(shí)現(xiàn)方法交叉 這種做法很安全,你只需要確保相關(guān)的方法交叉在一個(gè) dispatch_once 中就好了(這也是最推薦的做法)。
2.在 app delegate 中實(shí)現(xiàn)方法交叉 不像上面通過(guò)類(lèi)擴(kuò)展進(jìn)行方法交叉,而是簡(jiǎn)單地在 app delegate 的 application(_:didFinishLaunchingWithOptions:) 方法調(diào)用時(shí)中執(zhí)行相關(guān)代碼也是可以的。基于對(duì)類(lèi)的修改,這種方法應(yīng)該就足夠確保這些代碼會(huì)被執(zhí)行到。
最后,提醒大家,在不得已的情況下才去使用 Objective-C runtime。隨便修改基礎(chǔ)框架或所使用的三方代碼會(huì)給項(xiàng)目造成很大的影響。請(qǐng)務(wù)必要小心哦。
文章來(lái)源:Swift&Object-c Runtime
備注:本文已經(jīng)得到原作者的同意,授權(quán) OneAPM 技術(shù)博客進(jìn)行轉(zhuǎn)載
OneAPM Mobile Insight 以真實(shí)用戶(hù)體驗(yàn)為度量標(biāo)準(zhǔn)進(jìn)行 Crash 分析,監(jiān)控網(wǎng)絡(luò)請(qǐng)求及網(wǎng)絡(luò)錯(cuò)誤,提升用戶(hù)留存。訪(fǎng)問(wèn) OneAPM 官方網(wǎng)站感受更多應(yīng)用性能優(yōu)化體驗(yàn),想閱讀更多技術(shù)文章,請(qǐng)?jiān)L問(wèn) OneAPM 官方技術(shù)博客。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/17490.html
摘要:正在失業(yè)中的課多周刊第期我們的微信公眾號(hào),更多精彩內(nèi)容皆在微信公眾號(hào),歡迎關(guān)注。若有幫助,請(qǐng)把課多周刊推薦給你的朋友,你的支持是我們最大的動(dòng)力。是一種禍害譯本文淺談了在中關(guān)于的不好之處。淺談超時(shí)一運(yùn)維的排查方式。 正在失業(yè)中的《課多周刊》(第3期) 我們的微信公眾號(hào):fed-talk,更多精彩內(nèi)容皆在微信公眾號(hào),歡迎關(guān)注。 若有幫助,請(qǐng)把 課多周刊 推薦給你的朋友,你的支持是我們最大的...
閱讀 2279·2021-11-22 09:34
閱讀 1412·2021-10-11 10:59
閱讀 4526·2021-09-22 15:56
閱讀 3432·2021-09-22 15:08
閱讀 3471·2019-08-30 14:01
閱讀 853·2019-08-30 11:16
閱讀 1182·2019-08-26 13:51
閱讀 2969·2019-08-26 13:43