SDK開發中咱們可能但願使用已有的第三方開源庫,好比在發送請求的功能上咱們更但願用AFNetworking而非直接使用NSURLSession,又如在實現socket鏈接時咱們更但願用SocketRocket而非本身從零實現。但若是咱們直接把AFNetworking的源文件拖到靜態庫SDK裏,而宿主APP也引入了AFNetworking,這時運行代碼就會報符號衝突(duplicate symbols)的錯誤。html
這時大部分人的解決方案都是手動修改引入到SDK裏的開源庫代碼,包括類名、分類名、全局常量名、協議名等會致使衝突的符號。其實對於像AFNetworking(v3.2.1)這種源碼量較少的第三方庫來講,須要修改的地方都要多達47個,可想而知這是一項多麼低效和易錯的解決方案,並且若是下次須要升級SDK中的該第三方庫,你須要再從新手動改一遍……下邊咱們來一步步深刻解決這件麻煩事。
首先咱們考慮下怎樣避免每次都要修改第三方庫源碼,若是有一個單獨的文件來存原符號和重命名符號的對應關係就行了,咱們天然而然地會想到用宏定義。建立一個頭文件,好比叫XNGNamespace.hpython
// XNGNamespace.h #define AFURLSessionManager XNGURLSessionManager #define AFNetworkingReachabilityDidChangeNotification XNGNetworkingReachabilityDidChangeNotification #define AFImageResponseSerializer XNGImageResponseSerializer ...
而後在你的SDK工程中,若是你已經有一個預編譯頭文件(通常爲xxx.pch),在最上一行引入XNGNamespace.h,不然在Build Settings -> Prefix Header配置該文件的路徑(即把這個文件做爲預編譯頭文件)。這時你能夠在Xcode中看到本來的好比AFURLSessionManager
類名顏色變成和宏同樣的顏色,準確地說,這個類如今其實叫XNGURLSessionManager
了。linux
如今有了這個文件後,即便要升級SDK中的第三方庫,咱們也只須要在這個文件裏作少許增刪了。
但到目前爲止最麻煩的這部分事還沒解決,畢竟如今仍是要靠肉眼找出那些符號,手動編寫宏定義。有沒有什麼命令或腳本幫咱們分析出這些符號呢,這正好能夠藉助nm命令了。nm是Linux下用於查看指定文件(對象文件、可執行文件或對象文件庫)中符號列表的命令,因此爲了用這個命令,咱們須要先作點準備工做。c++
如上所述,咱們須要獲得一個可供nm命令分析的文件。新建一個庫工程,Framework類型或Static Library類型均可以,將第三方庫的源碼拖入其中,運行產出靜態庫文件。由於後邊分析也是直接跑在MacOS上,因此這裏直接產出當前架構的debug模式庫便可。若是是Static Library類型,咱們須要的直接就是.a文件,若是是Framework類型,咱們須要的是.framework中的那個同名文件。這兩種文件分析起來無差別,下文統一用.a的狀況來講明。git
不瞭解nm命令的同窗能夠先看下這個Tutorial,也建議看下完整的man page。下面以分析AFNetworking庫爲例,假如咱們的庫名叫libMyAFNetworking,cd到所在目錄執行nm libMyAFNetworking
,便可獲得每一個.o文件中的符號。下圖截取了AFURLRequestSerialization.o中的部分符號。github
經過nm命令的文檔,咱們瞭解到.o文件中頻繁出現的幾種符號是以下定義:面試
對於每個符號來講,其類型若是是小寫的,則代表該符號是local的;大寫則代表該符號是global(external)的。數據結構
- B 該符號的值出如今非初始化數據段(bss)中。例如,在一個文件中定義全局static int test。則該符號test的類型爲b,位於bss section中。其值表示該符號在bss段中的偏移。通常而言,bss段分配於RAM中。
- D 該符號位於初始化數據段中。通常來講,分配到data section中。
例如:定義全局int baud_table[5] = {9600, 19200, 38400, 57600, 115200},會分配到初始化數據段中。- S 符號位於非初始化數據區,用於small object。
- T 該符號位於代碼區text section。
- U 該符號在當前文件中是未定義的,即該符號的定義在別的文件中。
例如,當前文件調用另外一個文件中定義的函數,在這個被調用的函數在當前就是未定義的;可是在定義它的文件中類型是T。可是對於全局變量來講,在定義它的文件中,其符號類型爲C,在使用它的文件中,其類型爲U。
如今咱們先不考慮category屬性的getter和setter這種私有方法(下文會單獨說明),因此只關注類型是大寫字母的符號。咱們能夠很容易的概括出架構
_OBJC_CLASS_$_
開頭的是類名,以__OBJC_LABEL_PROTOCOL_$_
開頭的是協議名,只如下劃線_
開頭的是全局常量名_
開頭的是全局函數名__OBJC_PROTOCOL_$_
開頭的是協議名,不過咱們直接用S的規則就能夠了。D類型其實也存在以_OBJC_CLASS_$_
開頭的類名和如下劃線_
開頭的全局常量名,上邊樣例文件中未給出。有了目標後,咱們就能夠對於每行符號,用正則[0-9a-f]{16} [STD] (_OBJC_CLASS_\$|__OBJC_LABEL_PROTOCOL_\$)?_([_A-Za-z][^_]\w+)\n
來匹配獲得目標符號了。但這裏還有個比較坑的問題,對於D類型的符號,能夠看到蘋果官方SDK中的協議名也會被列出來,考慮到知名第三方庫通常不會和蘋果官方前綴相同,因此我會過濾掉以官方前綴(如NS、UI、WK等等)開頭的協議名。socket
有些第三方庫包含C++代碼,因爲編譯器的name mangling機制,直接用nm命令只能看到更改後的函數名。咱們能夠用Linux的另外一個命令c++filt
顯示本來的函數名。
// 一樣是PLCrashAsyncDwarfEncoding.o // nm libCrashReporter-iOS.a T __ZN7plcrash3PL_5async18dwarf_frame_reader4initEP21plcrash_async_mobjectPK23plcrash_async_byteorderbb // nm libCrashReporter-iOS.a | c++filt T plcrash::PL_::async::dwarf_frame_reader::init(plcrash_async_mobject*, plcrash_async_byteorder const*, bool, bool)
不過要注意下,若是加了c++filt的pipe,獲得的符號列表中,t類型會變爲"unsigned short",下邊咱們分析category屬性時要注意這點。
咱們先考慮下對於category,哪些符號會衝突。首先分類名確定是要改的,可是隻保證分類名不一樣就萬事大吉了嗎?不一樣分類中的方法和屬性都是往主類的方法列表和屬性列表中插入的,若是咱們SDK中使用的第三方庫版本和宿主APP使用的版本不一致,就可能存在分類方法名相同但方法實現不一樣,分類屬性名相同但關聯對象存取策略不一樣,致使代碼邏輯錯誤。因此除了分類名,咱們還有必要修改分類的屬性名和方法名。
下面的例子是SDWebImage的UIImage+ForceDecode.o文件中的符號
這樣咱們能夠用另外一個正則[0-9a-f]{16} unsigned short [+-]\[\w+\((\w+)\) ([\w:]+)\]\n
匹配分類中所須要的符號了。這裏要注意咱們只根據屬性的getter方法獲得屬性名,而不要把setter方法也加入到須要修改的符號行列。
咱們已經經過第二步的分析獲取到了全部須要重定義的符號,除category屬性外,遍歷一遍,將加了前綴的符號宏定義爲原始符號。對於category屬性則須要點額外操做,能夠想象下屬性名爲foo
,若是要加前綴XN
,那麼它的getter方法是直接加前綴爲-XNfoo
,但setter方法不是直接加前綴變爲-XNsetFoo:
,而應該是-setXNfoo:
了。
分析和產出的過程我已經寫了個Python腳原本作,代碼放在這裏https://github.com/xuning0/RedefineSymbols
。用法的話,好比你要分析的是libMySDWebImage.a,要加的命名空間前綴是ABC,那麼執行python3 redefine_symbols.py --ns ABC libMySDWebImage.a
,便可在當前目錄產出ABCNamespace.h文件。如上文所說將其拖入你的SDK工程,設置爲預編譯頭文件或在已存在的預編譯頭文件第一行import。如下截圖就是針對SDWebImage產出的ABCNamespace.h部分樣例。
這個腳本能夠覆蓋絕大部分狀況,但因爲OC屬性命名的特殊性,在拿到產出文件後最好人工覈查category getter和setter這部分的正確性。
020 持續更新,精品小圈子每日都有新內容,乾貨濃度極高。
結實人脈、討論技術 你想要的這裏都有!
搶先入羣,跑贏同齡人!(入羣無需任何費用)
BAT大廠面試題、獨家面試工具包,
資料免費領取,包括 數據結構、底層進階、圖形視覺、音視頻、架構設計、逆向安防、RxSwift、flutter,