本文是投稿文章,作者:我是喬忘記瘋狂
起因
最近練習一個項目,經典的UITabBarController加UINavigationController的組合,茫茫多得頁面需要設置一個統一的背景色,起初在每個控制器的viewDidLoad方法中都加上這麼一段:
self.view.backgroundColor = WXGGlobalBackgroundColor; // 設置全局背景色
可是隨著開發的進行,控制器和界面越來越多,每一個控制器都要寫這麼一句同樣的代碼讓我感覺很煩,於是開始尋找一勞永逸的辦法。經旁人指點,這其實跟我們想要黑盒測試一個方法一樣,不管控制器的viewDidLoad方法做了什麼,最後都給他加上設置背景色的代碼就OK了。於是馬上想到用OC運行時中的Method Swizzing來搞。
經過
有了解決問題的思路,剩下的事就很簡單了。我就直接貼代碼了:
// UIViewController+Extension.h #import @interface UIViewController (Extension) @end // // UIViewController+Extension.m #import "UIViewController+Extension.h" #import @implementation UIViewController (Extension) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Method originalMethod = class_getInstanceMethod([self class], @selector(viewDidLoad)); Method swizzledMethod = class_getInstanceMethod([self class], @selector(swizzled_viewDidLoad)); BOOL didAddMethod = class_addMethod([self class], @selector(viewDidLoad), method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod([self class], @selector(swizzled_viewDidLoad), method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } }); } - (void)swizzled_viewDidLoad { [self swizzled_viewDidLoad]; // self.view.backgroundColor = WXGGlobalBackgroundColor; // NSLog(@"%@ loaded", self); if (![self isKindOfClass:NSClassFromString(@"UIInputWindowController")]) { self.view.backgroundColor = WXGGlobalBackgroundColor; } } @end
關於Method Swizzing的使用和最佳實踐,還是推薦去看Mattt大神的文章吧,鏈接在這裡,英文不好的同學可以看南峰子前輩翻譯好的,文章鏈接在這裡。
Method Swizzing的用法並不難,我就不過多解釋了。說一下碰到的問題:第一次寫完測試運行的時候,發現模擬器整個界面只有純色一片,看不到任何控件,進調試工具一看,所有控件都在也都正常,而且在模擬器上點擊對應控件的位置,仍然能夠觸發事件,整個界面就好像被人為塗了一層顏色一樣。一時不知該怎麼解決,只好NSLog一下,看看都有哪些控制器觸發了viewDidLoad方法,然後就真的發現了一個奇怪的家伙:
// 省略上面的輸出結果 loaded
而且這個家伙還是在最後一個,頓時對它懷疑大增,弄不好就是這家伙弄了個全屏的view給我塗在了屏幕上面。接下來就測試一下是不是它搞的鬼,代碼同上面的最終代碼,最後測試運行發現排除掉它之後界面立馬正常了,真的就是它搞的鬼。
結果
最後只能去查這個UIInputWindowController到底是個什麼東西,可惜沒有查到直接的結果,只知道它是一個蘋果的私有API,而且網上已經有人用運行時技術列出了它所包含的成員變量和方法。至於它到底是做什麼用的,我們可以從它的命名大致猜一下,多少應該是跟鍵盤輸入有關。我們都知道彈出的鍵盤也是一個UIWindow對象,不過有別於我們經常使用的keyWindow,那麼這些window對象誰來管理呢?可能這是一個思路。
最近時間有限,要繼續練習項目了,這個問題就暫時解決到這裡,就算是一次對OC運行時和Method Swizzing的小實踐吧。