private_external_symbol VS external_symbol

騰訊提供的iOS SDK ——TencentOpenApi 是靜態庫,然后因為業務需求,需要把它轉換為動態庫(取名:DynamicTencentOpenApi)。在實際開發時,發現了一個奇怪的事情:

整個工程如下:
(有興趣的讀者,可直接下載該工程,進行實驗)

DynamicTencentOpenApi Project.png

Demo 的測試 DynamicTencentOpenApi.Framework 的主要代碼如下:

    // 測試TencentOAuth
    NSString *version = [TencentOAuth sdkVersion];
    NSLog(@"%@",version);

    // 測試QQApiInterface
    NSString *qqInstallUrl = [QQApiInterface getQQInstallUrl];
    NSLog(@"%@",qqInstallUrl);

運行編譯工程的 DynamicTencentOpenApi Target 和 Demo Target,得到結果如下:

  • DynamicTencentOpenApi.Framework 編譯運行成功
  • Demo 編譯鏈接失敗,報錯誤:Undefined symbols for architecture x86_64: "_OBJC_CLASS_$_QQApiInterface"

這里奇怪的地方在于:

  • DynamicTencentOpenApi.Framework 編譯運行成功
  • Demo 編譯鏈接時,TencentOAuth 的 symbol 能找到,QQApiInterface 的 symbol 卻找不到?

MachOView 查看 DynamicTencentOpenApi.Framework 里的 binary 文件發現:

TencentOAuth Symbol.png
QQApiInterface Symbol.png
  • _OBJC_CLASS_$_QQApiInterface symbol 的類型是:N_PEXT
  • _OBJC_CLASS_$_TencentOAuth symbol 的類型是:N_EXT

N_PEXTN_EXT 的區別是:

N_PEXT (0x10)—If this bit is on, this symbol is marked as having limited global scope. When the file is fed to the static linker, it clears the N_EXT bit for each symbol with the N_PEXT bit set. (The ld option -keep_private_externs turns off this behavior.) With macOS GCC, you can use the private_extern function attribute to set this bit.

N_EXT (0x01)—If this bit is on, this symbol is an external symbol, a symbol that is either defined outside this file or that is defined in this file but can be referenced by other files.

https://developer.apple.com/documentation/kernel/nlist_64

翻譯過來,這2者的區別是:

  • N_PEXT ,意思是:private_extern。該類型的 symbol 在指定范圍內可見
  • N_EXT ,意思是:extern。該類型的 symbol,在任何范圍內均可見

關于范圍如圖所示:

N_PEXT和N_EXT的范圍.png

因此,在編譯Demo-OC時,到鏈接階段時,就找不到這個_OBJC_CLASS_$_QQApiInterface symbol,繼而編譯失敗。

那么如何解決這個問題呢?

創建一個QQApiInterface鏡像類 ,鏡像類 wrap QQApiInterface,對外提供和QQApiInterface一致的接口,在內則把對應的方法響應轉發給QQApiInterface。具體可看例子中的鏡像類 QQApiInterfaceMirror

原來準備按照這個方案實現 DynamicTencentOpenApi ,但是后面發現,TencentOpenApi SDK 里不止 QQApiInterface 的 symbol 是N_PEXT類型,還有QQApiTextObjectQQApiExtendObjectQQApiImageObjectQQApiWebImageObject 等類的 symbol 都是N_PEXT類型的。考慮到開發成本(為這些類創建同等的鏡像類)和維護成本(TencentOpenApi SDK 升級后,每個鏡像類都得重新測試),最終是放棄把 TencentOpenApi 從靜態庫轉換到動態庫 DynamicTencentOpenApi。

這種編譯鏈接的錯誤(symbol的范圍受限導致鏈接失敗),相對來說,是比較少見的,因而花了很多時間去查,才找到了原因。希望這篇文章能讓后面的來者可以避開此坑。enjoy~


參考資料:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容