1. 程式人生 > >iOS 動態庫和靜態庫的的區別 動態庫的隔離與靜態庫的吸附問題以及解決方法

iOS 動態庫和靜態庫的的區別 動態庫的隔離與靜態庫的吸附問題以及解決方法

  • 起因

  • 理論功底

    • 動態庫和靜態庫

      • 介紹

      • 靜態庫和動態庫的區別

      • 舉個例子, iOS 專案中使用 Embeded Framework

      • 靜態庫和動態庫如何構建和載入

      • 靜態庫和動態庫依賴關係

    • Xcode 專案結構

      • iOS 依賴管理事實上的標準

    • 解決問題

      • 製作動態庫

    • 剖析下動態庫 Framework 吧

      • 回過頭來看 Embened Framework

      • Why Swift does not Support Staic Libraies

      • CocoaPods 使用 Use_framework!

      • 動態庫 Framework 的檔案結構

        • 更愉快的匯入檔案

          資源問題

  • 參考

起因

去年,公司iOS端,之前由於所有的業務端程式碼都是混亂管理,造成開發有很多痛點無法單測,團隊成員提交程式碼衝突機率大,CI配合效果差,功能性程式碼多端無法複用,單倉庫程式碼量大,編譯時間長 等等痛點,領導和組內多次溝通開始著手元件化開發,希望能改進這些開發中的痛點,成立元件化團隊。

元件化的方案大同小異,基礎性程式碼封裝私有庫,業務元件互動交由中介軟體負責,專案依賴工具用iOS專案事實上的標準CocoaPods

前期的基礎性元件拆分都較為順利,從依賴樹的葉子節點開發是最合適的方案。

隨著元件抽離的越來越多,私有庫的依賴體系也越來越複雜,慢慢過渡到了業務元件。業務元件用了Swift的第三方元件,用了Swift庫的同學都知道必須加上use_frameworks!,這個標記是說Pod管理的依賴全部編譯為動態庫,然後呢我們的很多元件又依賴了諸如百度地圖,微信分享等靜態庫,於是我在執行 pod install 報了一個沒有碰見過的錯誤。

1 [!] The 'Pods-LJA_Example' target has transitive dependencies that include static binaries:

installError.png

這就尷尬了,於是一陣瘋狂的搜尋google stackoverflow等,然而並沒有什麼卵用,而且上面催得急,根本沒時間處理這些小問題 業務重構是最主要的,以至於我們的業務元件沒有做到獨立倉庫拆分。

直到最近終於找到了解決辦法:( 主要是自己的功力不夠深厚)

理論功底

動態庫和靜態庫

介紹

首先靜態庫和動態庫都是以二進位制提供程式碼複用的程式碼庫

  • 靜態庫 常見的是 .a

  • 動態庫常見的是 .dll(windows),.dylib(mac),so(linux)

  • framework(in Apple): Framework 是Cocoa/Cocoa Touch程式中使用的一種資源打包方式,可以將程式碼檔案、標頭檔案、資原始檔、說明文件等集中在一起,方便開發者使用。也就是說我們的 framework其實是資源打包的方式,和靜態庫動態庫的本質是沒有關係的

靜態庫和動態庫的區別

靜態庫: 連結時會被完整的複製到可執行檔案中,所以如果兩個程式都用了某個靜態庫,那麼每個二進位制可執行檔案裡面其實都含有這份靜態庫的程式碼

動態庫: 連結時不復制,在程式啟動後用dyld載入,然後再決議符號,所以理論上動態庫只用存在一份,好多個程式都可以動態連結到這個動態庫上面,達到了節省記憶體(不是磁碟是記憶體中只有一份動態庫),還有另外一個好處,由於動態庫並不繫結到可執行程式上,所以我們想升級這個動態庫就很容易,windows和linux上面一般外掛和模組機制都是這樣實現的。

But我們的蘋果爸爸在iOS平臺上規定不允許存在動態庫,並且所有的 IPA 都需要經過蘋果爸爸的私鑰加密後才能用,基本你用了動態庫也會因為簽名不對無法載入,(越獄和非 APP store 除外)。於是就把開發者自己開發動態庫掐死在幻想中。

直到有一天,蘋果爸爸的iOS升級到了8,iOS出現了APP Extension,swift程式語言也誕生了,由於iOS主APP需要和Extension共享程式碼,Swift語言的機制也只能有動態庫,於是蘋果爸爸尷尬了,不過這難不倒我們的蘋果爸爸,畢竟我是爸爸,規則是我來定,我想怎樣就怎樣,於是提出了一個概念Embedded Framework,這種動態庫允許APP和APP Extension共享程式碼,但是這份動態庫的生命被限定在一個APP程序內。簡單點可以理解為被閹割的動態庫。

舉個例子,iOS專案中使用Embeded Framework

如果你把某個自己開發的動態庫(系統的不算,畢竟蘋果是爸爸)放在了Linked Frameworks and Libraries裡面,程式一啟動就會報Reason: Image Not Found,你只能把它放在Embeded Binaries裡面才能正常使用,

看圖: 

useEmbededFramework.png

靜態庫和動態庫如何構建和載入

簡單點,說話的方式簡單點~~

上面的介紹貌似有點抽象啊套用在美團技術分享大會上的話就是:

  • 靜態庫: 一堆目標檔案(.o/.obj)的打包體(並非二進位制檔案)

  • 動態庫: 一個沒有main函式的可執行檔案

這裡我們來複習下C語言的基本功,編譯和連結

  • 編譯: 將我們的原始碼檔案編譯為目標檔案

  • 連結: 將我們的各種目標檔案加上一些第三方庫,和系統庫連結為可執行檔案。

由於某個目標檔案的符號(可以理解為變數,函式等)可能來自其他目標檔案,其實連結這一步最主要的操作就是決議符號的地址。

  • 若符號來自靜態庫(本質就是.o 的集合包)或 .o,將其納入連結產物,並確定符號地址

  • 若符號來自動態庫,打個標記,等啟動的時候再說---交給dyld去載入和連結符號

於是連結加裝載就有了不同的情況

  • Load 裝載:將庫檔案載入記憶體

    Static Loading:啟動時

    Dynamic Loading:啟動後(使用時)

  • Link 連結:決議符號地址

    Static Linking:構建(連結)時

    Dynamic Linking:執行時(啟動時或使用時)

然後組合起來就是 2 * 2 = 4 了

  • Static Loading + Static Linking

  • Static Loading + Dynamic Linking

  • Dynamic Loading + Dynamic Linking

  • ~~Dynamic Loading + Static Linking~~

第一種是純靜態庫相關了

第二種就是靜態載入(啟動時),動態連結,連結時,動態庫參與連結,但是這時候只是給符號打了標記告訴我這個符號來自與動態庫,程式啟動時,iOS或者Mac OS作業系統的dyld自動load + link。

既然全部都是自動的。那麼符號的呼叫方完全不知道你到底是原始碼還是靜態庫,動態庫 。

第三種收到呼叫dlopen + performSelector通常iOS的APP不適用這裡不討論

第四種,沒見過,個人也不是特別懂

有需求請參看文後的程式設計師的自我修養一書

靜態庫和動態庫依賴關係

既然有 2 種庫,那麼依賴關係又是 2 * 2 嘍

  • libA.a dependency libB.a

  • UIKit.dylib dependency Foundation.dylib

  • libA.a dependency Foundation.dylib

  • MyXX.dylib dependency libA.a

第一種 靜態庫互相依賴,這種情況非常常見,製作靜態庫的時候只需要有被依賴的靜態庫標頭檔案在就能編譯出來。但是這就意味者你要收到告訴使用者你的依賴關係

幸運的是CocoaPod就是這樣做的

第二種動態庫依賴動態庫,兩個動態庫是相互隔離的具有隔離性,但是製作的靜態庫的時候需要被依賴動態庫參與連結,但是具體的符號決議交給dyld來做。

第三種,靜態庫依賴動態庫,也很常見,靜態庫製作的時候也需要動態庫參與連結,但是符號的決議交給dyld來做。

第四種,動態庫依賴靜態庫,這種情況就有點特殊了。首先我們設想動態庫編譯的時候需要靜態庫參與編譯,但是靜態庫交由dyld來做符號決議,but這和我們前面說的就矛盾了啊。靜態庫本質是一堆.o 的打包體,首先並不是二進位制可執行檔案,再者你無法保證主程式把靜態庫參與連結共同生成二進位制可執行檔案。這就尷尬了。

怎麼辦?

目前的編譯器的解決辦法是,首先我無法保證主程式是否包含靜態庫,再者靜態庫也無法被dyld載入,那麼我直接把你靜態庫的.o 偷過來,共同組成一個新的二進位制。也被稱做吸附性

那麼我有多份動態庫都依賴同樣的靜態庫,這就尷尬了,每個動態庫為了保證自己的正確性會把靜態庫吸附進來。然後兩個庫包含了同樣的靜態庫,於是問題就出現了。 看到這裡想必前面出現的錯誤你已經能猜出來了把~_~

後面再詳細解釋

先來個總結

可執檔案(主程式或者動態庫)在構建的連結階段

  • 遇到靜態庫,吸附進來

  • 遇到動態庫,打標記,彼此保持獨

Xcode 專案結構

  • target:對於一個產物(app,.a ,.framework)

  • project:一個專案包含多個 target

  • workspace: 一個包含多個 target

  • schema: 指定了一個產物是按照何種的依賴關係,編譯-連結到最終的一個產物

iOS 依賴管理事實上的標準

這麼多年,Apple的部落格和文件也就告訴了我們什麼是靜態庫什麼是動態庫,如何製作等。但是並沒有給我們提供一系列的依賴管理工具。所以CocoaPods成了事實上的標準。

通常CocoaPods管理的工程結構如下:

QQ截圖20170427095936.png

那麼當我們按下CMD + B的時候,整個專案按照先編譯被依賴Pod,然後依賴其他Pod的Pod也被構建出來,最終所有的元件被編譯為一個lib-Pods-XXXAPP.a被新增進專案進去。資源通過CocoaPods提供的指令碼也一併被複制進去。想了解CocoaPods做了什麼的讀者可以參看後面的連結

解決問題

這麼多理論功底的建立,相信我們已經能分析出來之前pod install的原因了。就是用了use_framework那麼我們的所有Pod都會以動態庫(Embeded Framework)的形式去構建,於是那些非開源的庫(如百度地圖,微信分享)如果被多個Pod依賴(元件化開發中太常見了)於是被吸附到動態庫裡面,所以CocoaPod直接就不讓我們install成功。因為你現在的依賴管理就是錯誤的。

在聽取美團葉樉老師分享的時候 他們的出發點是因為要繞過蘋果爸爸在iOS9以下對__text 段60M的限制使用了動態庫方案,我們是因為某些swift庫必須要用到(歷史遺留原因)動態庫。美團的做法是摘除依賴關係,自定義CocoaPods(開源的本來就是用著不爽我就改)。但是我是個小菜雞啊。我也不會 ruby(以後會學的),但是葉樉老師給我提了別的idea。前面我們知道 動態庫和動態庫是隔離性,動態庫依賴靜態庫具有吸附性,那麼我們可以自定義一個動態庫把百度地圖這種靜態庫吸附進來。對外整體呈現的是動態庫特性。其他的元件依賴我們自定義的動態庫,由於隔離性的存在,不會出現問題。

製作動態庫

1 建立動態庫專案這裡以 wx 舉例

createDynamicFramework.png

2 按照微信的官方文件。新增依賴庫(我是因為pod install巨慢所以我直接拽進來了)

wxDdependency.png

3 將wx的PublicHeader暴露出來,注意由於我並沒有使用到wx相關API所以連結器幫我們連結動態庫的時候可能並不會把wx靜態庫吸附進來。我們手動在build Setting的other link flags加上-all_load標記

publicHeader.png

4.在Schema裡面跳轉編譯配置為Release,並且選擇所有的CPU架構

SchemaRelease.png

buildArchive.png

5 然後選擇模擬器或者 Generic iOS Device 執行編譯就會生成對應版本的 Framework 了。

releaseFrameworl.png

6.但是為了保證開發者使用的時候是真機模擬器都能正常使用,我們需要合併不同架構

這裡在Build Phases裡新增以下指令碼,真機和模擬器都Build一遍之後就會在工程目錄下生成Products資料夾,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 if "${ACTION}" "build" ]

相關推薦

ARM:Release版本Debug版本執行不一致的一種可能原因以及解決方法

現象:Debug下執行正常,Release下執行不符合預期。 經過定位,確定是程式中對CPU片內Flash操作導致。 可能原因:可能是在Release下經過優化,對內部Flash操作的位置或許會變化,影響到程式的儲存資料段。 解決方法:改用外部Flash作為儲存資料後,問題

iOS 動態靜態的的區別 動態隔離靜態吸附問題以及解決方法

起因 理論功底 動態庫和靜態庫 介紹 靜態庫和動態庫的區別 舉個例子, iOS 專案中使用 Embeded Framework 靜態庫和動態庫如何構建和載入 靜態庫和動態庫依賴關係

強型別的動態遊標弱型別的動態遊標區別

1、簡單的來說:強型別的動態遊標是指帶有return返回語句的,而弱型別的動態遊標是指不帶return語句的(也即,弱型別的動態遊標可以與任何查詢語句匹配,但是強型別的動態遊標只能與特定的查詢語句匹配。) 2、個人理解:強型別的有點像java中使用了泛型一樣對其進行了限制,

在使用 Spring Boot MyBatis 動態切換資料來源時遇到的問題以及解決方法

1. org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)在使用了動態資料來源後遇到了該問題,從錯誤資訊來看是因為沒有找到 *.xml 檔案而導致的,但是在配置檔案中 確實添加了相關的配置,這種錯誤的原因

節點2上crsd無法啟動,數據監聽無法自動啟動,比如ocrconfig、ocrcheck以及srvct

oracle 數據庫 操作系統 信息 手工 CRSD進程在11g中的變化在11.2中,CRSD進程不再是RAC中最關鍵的進程之一。如果對10g RAC比較熟悉,應該清楚CRSD進程的重要性,Oracle在操作系統啟動後,就是通過啟動這個進程然後啟動整個CLUSTER以及數據庫的。在11.2

go get獲取的依賴dep獲取的vendor目錄下的依賴不一致的問題------玩下Gopkg.toml

        程式碼: package main import ( "fmt" "github.com/satori/go.uuid" ) func main() { u1 := uuid.Must(uuid.NewV4()) fmt.P

ios應用內嵌h5頁面數據自動變色識別為手機號碼的解決方法——手機號碼撥號禁用IOS手機頁面數字自動識別為手機號

log 數字 bsp 標簽 one .com div meta name 現象如下,ios應用內嵌h5頁面,本來是設置了白色的數字,兩三秒之後會自動變為黑色,然後點擊的時候就會彈出是否撥號的提示; 解決方法,添加如下meta標簽,即可解決: <meta

C++常用函數&&C++實用技巧模版 PDF文件

模版 jpg clas 詳細 body 鏈接 https 信息 實用 如題所示 分享一些函數 pdf文件來自《信息學奧賽一本通》 詳細請見鏈接: https://pan.baidu.com/s/1jKqwH50 密碼: t28b C++常用庫函數&&C

Windows 7下用arp命令繫結IPMAC地址,提示“ARP 項新增失敗: 拒絕訪問”的解決方法

在Win 7版本以管理員身份執行時提示:“ARP 項新增失敗:請求的操作需要提升。”    解決辦法: CMD中輸入:netsh i  i show in  //注意兩個i之間是有空格的 然後找到“本地連線”對應的 “Idx” (我的是

IOS系統iphone x/iphone7plus怎麼上line、WhatsApp、Skype用不了解決方法

一直用蘋果手機的朋友發現最近不能用line、WhatsApp、Skype聊天了,今天小編就來教大家如何解決這個問題! 設定方法: 1、設定中點選,設定-通用-vpn 2、新增vpn配置。 3、選擇ipsec,描述資訊隨便輸入,填寫伺服器地址,賬戶和密碼,金鑰,其

Java多執行緒----執行緒的同步,鎖死鎖,問題以及解決方法(例子說明)

一、執行緒併發同步概念 執行緒同步其核心就在於一個“同”。所謂“同”就是協同、協助、配合,“同步”就是協同步調昨,也就是按照預定的先後順序進行執行,即“你先,我等, 你做完,我再做”。 執行緒同步,就是當執行緒發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不會返回,其他

iOS 12 更新 遇到的坑以及解決方法

1. 升級iOS12 編譯遇到 : iPhone has denied the launch request. 解決方法 修改以下證書的信任狀態:鑰匙串 -> 證書 -> 顯示簡介 -> 信任-> 將始終信任改為使用系統預設 Develo

JSONJSONP劫持以及解決方法

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興! json劫持 json劫持攻擊又為”JSON Hijacking”,攻擊過程有點類似於csrf,只不過csrf只管傳送http請求,但是js

echart在ios部分機型報錯null is not object(evaluating 'ctx.save')問題解決方法

觸發背景:    在使用wex5平臺開發和使用xcode打包後的app上點選返回鍵再進去載入echart圖反覆3次左右必現 報錯如圖   解決方法: 多次檢查後,定位為退出頁面後沒有銷燬原物件,再次進入渲染導致的問題 在程式碼裡面加上離

接入ShareSDK第三方登入分享遇到的問題以及解決方法

在出現這些問題前你已經完成了以下工作任務:     1、在微信開放平臺註冊帳號,並且建立了應用並稽核通過了; 2、下載了ShareSDK並且接入到了自己的專案中; 3、已經按官方文件填寫了功能程式碼。

wordpress 設定靜態後標籤中中文連結找不到頁面的解決方法

轉自:http://www.li.cm/news/2011/04/17/wordpress-%E8%AE%BE%E7%BD%AE%E9%9D%99%E6%80%81%E5%90%8E%E6%A0%87%E7%AD%BE%E4%B8%AD%E4%B8%AD%E6%96%87%

多執行緒併發問題以及單例設計模式執行緒安全以及同步方法同步程式碼塊

執行緒安全和非執行緒安全 在作業系統中,執行緒是不擁有資源的,程序擁有資源。執行緒是由程序建立的,一個程序可以建立多個執行緒,這些執行緒共享程序中的資源。當多個執行緒同時操作一個變數時,這個時候就可能會造成資料的不一致性,此時就是執行緒不安全。 JVM有主記

iOS 12 在系統中文鍵盤上使用 AutoFill 會遇到詭異的問題以及解決方案

感謝我們 iOS 團隊的趙恆、劉家飛發現此 bug。一. 問題描述首先,建立一個 textFie

iOS 常見錯誤之 linker command failed with exit code 1 並且點不進去 解決方法

經常遇到這種問題,工程中只報這一個錯誤,而且點選錯誤,右邊的不顯示錯誤詳情描述: 在網上搜索了很多方法貌似都沒啥大用處,最後看到一個說是:找到Build settings->Linking-

Linux環境下gcc靜態編譯/usr/bin/ld: cannot find -lc錯誤原因及解決方法 原因:

原因: 一般出現這個問題的時候,Makefile中肯定有-static選項。這其實是靜態連結時沒有找到libc.a。 解決方案: 需要安裝glibc-static.xxx.rpm,如glibc-static-2.12-1.107.el6_4.2.i686.rpm,或是yum install gli