Objective-C運行時的一些技巧

來源:互聯網
上載者:User

標籤:style   blog   http   color   io   os   ar   使用   for   

Apple的iOS8發布以後,大家都開始了適配的工作了。但是這個過程總會遇到一些攔路虎,例如推送的API改動。可是商業項目上嵌入了各種各樣的第三方靜態庫,這些靜態庫品質參差不齊,其中一個靜態庫甚至在Xcode6上編譯後出現問題。於是只能使用Xcode5來編譯,但這樣就有一個很糾結的問題就是,UIMutableUserNotificationAction等一些類在舊版的Xcode要麼就是無法編譯,要麼只能用宏來跳過。

這時候,我還是想起了Objective-C的運行時方法,使用NSClassFromString(@"UIMutableUserNotificationAction")來擷取到系統的類。光這樣子還是有很多不足,因為這個類中有很多方法、屬性。雖然保證了啟動並執行正常,但是編寫這些方法還是有各種不便。例如各種performSelector:、objc_msgSend、setValue: forKey:,實在寫得很痛苦。我這裡用了一個比較取巧的方法,建立一個偽造的類如“XXXMutableUserNotificationAction”,繼承NSObject即可。然後將UIMutableUserNotificationAction所有的屬性和方法的聲明添加到XXXMutableUserNotificationAction的標頭檔。以後,使用UIMutableUserNotificationAction時,就如下方:

1 Class XXXMutableUserNotificationActionClass = NSClassFromString(@"UIMutableUserNotificationAction");2 XXXUIMutableUserNotificationAction *action = [[XXXMutableUserNotificationActionClass alloc] init];

既可以使用Xcode的補全提示,又可以通過編譯。(如果大家有更好的方法,歡迎探討探討)

 

Object-C運行時的方法固然強大,但是使用這些方法還是有一定的風險。例如靜態分析對於一些運行時的問題是檢查不出來的,這裡我舉一個記憶體流失的例子。我的項目中使用了一些運行時添加屬性的方法,同時為一些View添加了block類型的屬性。在使用的時候,不小心在block中直接使用了self,就會出現編譯器無法檢查的記憶體泄露。泄漏路徑:VC->View->block->VC,形成了循環參考。這種泄漏相對隱蔽一些,但對於經常RAC的童鞋來說,可能已經練就到百毒不侵了(^_^)。因為@weakify和@strongify的頻繁使用,我對這類型泄漏已經比較敏感。__weak typeof(self) weakSelf = self,算是一種雖然難看,但是行之有效方法,如果有興趣也可以參考RAC的解決方案,本質上也是一樣的。
題外話,BlockKit包含了一個很好用的分類“NSObject+BKAssociatedObjects”,可以用更友好的方法實現運行時添加屬性,順帶一提bk_weaklyAssociateValue的實現思路相當巧妙,值得借鑒。

 

最後一個技巧是關於RAC和系統API的一些關係,先來看看一下兩段代碼:

1 UIGestureRecognizer *dismissKeyboardGR = [[UIGestureRecognizer alloc] init];2 [self.view addGestureRecognizer:dismissKeyboardGR];3 [[[self rac_signalForSelector:@selector(gestureRecognizer:shouldReceiveTouch:)4                  fromProtocol:@protocol(UIGestureRecognizerDelegate)]5   takeUntil:dismissKeyboardGR.rac_willDeallocSignal]6   subscribeNext:^(id x) {7       [Utils hideKeyboardInAllView];8   }];9 dismissKeyboardGR.delegate = self;
1 UIGestureRecognizer *dismissKeyboardGR = [[UIGestureRecognizer alloc] init];2 [self.view addGestureRecognizer:dismissKeyboardGR];3 dismissKeyboardGR.delegate = self;4 [[[self rac_signalForSelector:@selector(gestureRecognizer:shouldReceiveTouch:)5                  fromProtocol:@protocol(UIGestureRecognizerDelegate)]6   takeUntil:dismissKeyboardGR.rac_willDeallocSignal]7   subscribeNext:^(id x) {8       [Utils hideKeyboardInAllView];9   }];

先來看看這兩段代碼的區別只在於delegate的設定先後不一樣,但這就造成了後一段代碼在iOS6上就無法觸發RAC裡方法,iOS7上正常。為什麼呢?

這涉及到一個類似緩衝的機制。平時,我們會習慣使用respondsToSelector:來檢查一個對象或者類是否實現了對應的方法,但頻繁調用respondsToSelector:會對效能有一定的影響。特別是UITableView的dataSource一些方法,調用頻率很高的。因此,在設定delegate後,UIGestureRecognizer使用了respondsToSelector:檢查了一次self是否gestureRecognizer:shouldReceiveTouch:的方法,然後把這個結果緩衝起來。由於RAC也使用了類似Method Swizzling方法,因此在設定delegate以後再使用RAC的方法,UIGestureRecognizer也唯讀取了緩衝,並不會再次檢查,所以認為self並未實現gestureRecognizer:shouldReceiveTouch:的方法,於是不作調用。(具體緩衝的實現方法,可以參照http://www.cnblogs.com/ipinka/p/3862786.html)

Objective-C運行時的一些技巧

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.