1. 程式人生 > >iOS靜態庫中慎重使用Category擴充套件方法(selector not recongized)

iOS靜態庫中慎重使用Category擴充套件方法(selector not recongized)

事情背景

這個坑是前幾天踩的,踩的還特別是時候專案馬上要發版了,發給測試做最後的驗證,測試反饋從log上面看有點詭異,有些手機可以有些手機不可以。因為提測前我是做過自測的,我相信應該沒有問題,一同和測試一起繼續測試,發現還是有些手機可以有些手機不可以。這麼奇怪的問題讓我很懵逼呀。按照常理來說應該是要不可以就都不可以呀。因為這是一個請求裡面發生的當時我的本能反應是不是網路不穩定呀,但是話又說會來。這如果是網路的問題的話也不會這麼巧合呢?好吧 我又回到座位開始吭哧吭哧的查bug了,說好的發完版去健身的,啊啊啊又要泡湯了;因為最近肩膀又開始疼痛了(ps 程式設計師請保護好自己的身體),今天特意寫了一個demo來追蹤下原因。(普通的程式設計師解決問題,優秀的程式設計師追溯問題的根源,這句話好像哪裡有點不太對,開始查詢原因吧)。

問題排查

log全開,手機全部部署上看log,剛剛有問題的手機也沒有問題呀。我的哥,這什麼情況。百思不得其解。然後又用釋出demo測試了下,一看好像還真不行。我的天報異常了。

異常

這是什麼鬼,我明明寫了這個方法呀,為啥會說找不到呢。測試一直問我你是不是給我同樣的包呀,為啥之前可以現在不可以。我 我 我開始懷疑人生了。我保證我真的沒有改過任何程式碼除了關閉log。雖然問題找到了但是我還是不知道為啥會這樣。想了很久沒明白為啥會這樣呢。可事實擺在面前呀,就是報異常了。吃完飯回來,突然想到好像有點不一樣。之前給測試的包我是直接把sdk的工程直接引入到測試demo的。如下圖

直接引用工程專案

我相信很多sdk的開發者測試的時候應該都是這麼玩的,因為這樣便於除錯sdk的問題。可是對外demo裡面我引用的是靜態.a庫。

引用靜態庫

這是我目前唯一能想到的差別了。一測還真是,只有引入靜態庫的時候才會出問題,那問題應該是靜態庫在打包的時候這個方法沒有被新增到被擴充套件的類裡面。這個報錯的這個方法是很特別的,因為我為了通用所以就使用category特性擴充套件了NSString。網上一查在靜態庫中還真有問題,從文章中的原因我們可以看出是:

Unix的標準靜態庫實現和Objective-C的動態特性之間有一些衝突:Objective-C沒有為每個函式(或者方法)定義連結符號,它只為每個類建立連結符號。這樣當在一個靜態庫中使用類別來擴充套件已有類的時候,連結器不知道如何把類原有的方法和類別中的方法整合起來,就會導致你呼叫類別中的方法時,出現"selector not recognized",也就是找不到方法定義的錯誤。為了解決這個問題,引入了-ObjC標誌,它的作用就是將靜態庫中所有的和物件相關的檔案都載入進來

這就是問題的根源。但是另外一篇文章中說在64位的操作體統中連結器有一個bug,會導致只包含有類別的靜態庫無法使用-ObjC標誌來載入檔案。變通方法是使用-all_load 或者-force_load標誌,它們的作用都是載入靜態庫中所有檔案,不過all_load作用於所有的庫,而-force_load後面必須要指定具體的檔案。

尋找真凶

對於我上面的描述大家可能會認為問題的根源是我的不同引用方式造成的,直到我剛剛寫這個demo之前我也一直這麼認為的。但是事實真的是這樣嗎? NO ,事情的真相是我的開發demo裡面增加了-ObjC標誌,釋出demo裡面沒有。這才是真正的原因。因為在我剛剛寫CategoryExtendDemo的時候我發現不管我是直接引入工程還是引入靜態庫都會丟擲異常。所以說明這兩種引用方式沒問題。 上面的兩篇文章說了是沒有增加-ObjC標誌的原因。果真我在我的測試demo裡面增加這個標誌之後兩種方式都不會出問題。看來這才是真正的原因。果然我在我的開發demo裡面也發現了我確實增加了-ObjC標誌。

-ObjC標記

為了進一步測試如果擴充套件了自定義的類是否也會有同樣的問題,我在程式碼裡面測試了一個自定義的類

@interface CustomCategory : NSObject

@end
@implementation CustomCategory

@end

//擴充套件類
@interface CustomCategory(Extend)

- (void)extendMethod;
@end
@implementation CustomCategory(Extend)

- (void)extendMethod{
    NSLog(@"CustomCategory extendMethod");
}
@end

總結

1、在靜態庫中如果我們使用了category擴充套件方法,不管是系統的還是自定義的類如果沒有新增-Objc相關標誌,都會丟擲unrecognized selector sent to instance 異常

2、在測試sdk的時候一定要和釋出包操作環境一樣,不然真的不知道哪個環節坑了自己,最重要的是還坑了隊友。

3、在sdk這種要提供給第三方使用的程式碼裡面減少使用category這種類似黑科技的特性,因為我們不能確保使用者會增加-ObjC連結標誌,因為如果我們只是在靜態庫編譯的時候加上這個標誌,使用者沒有加上也同樣會丟擲異常

原地址: