組件化-動態(tài)庫實戰(zhàn)

原文地址 ,此簡書只做備份,強烈推薦原文,畢竟格式比簡書好看,還清晰


起因

去年,鏈家網(wǎng)iOS端,之前由于所有的業(yè)務(wù)端代碼都是混亂管理,造成開發(fā)有很多痛點無法單測 團隊成員提交代碼沖突機率大 CI配合效果差 功能性代碼多端無法復(fù)用 單倉庫代碼量大 編譯時間長 等等痛點,領(lǐng)導(dǎo)和組內(nèi)多次溝通開始著手組件化開發(fā),希望能改進這些開發(fā)中的痛點,成立組件化團隊。
組件化的方案大同小異,基礎(chǔ)性代碼封裝私有庫,業(yè)務(wù)組件交互交由中間件負(fù)責(zé),項目依賴工具用 iOS項目事實上的標(biāo)準(zhǔn) CocoaPods
前期的基礎(chǔ)性組件拆分都較為順利,從依賴樹的葉子節(jié)點開發(fā)是最合適的方案。
隨著組件抽離的越來越多,私有庫的依賴體系也越來越復(fù)雜,慢慢過渡到了業(yè)務(wù)組件。業(yè)務(wù)組件用了Swift的第三方組件,用了Swift庫的同學(xué)都知道必須加上use_frameworks!,這個標(biāo)記是說Pod管理的依賴全部編譯為動態(tài)庫,然后呢我們的很多組件又依賴了諸如百度地圖,微信分享等靜態(tài)庫,于是我在執(zhí)行 pod install 報了一個沒有碰見過的錯誤

[!] The 'Pods-LJA_Example' target has transitive dependencies that include static binaries: (/Users/nero/Desktop/Static_Dynamic/Componment/Example/Pods/libWeChatSDK/libWeChatSDK.a)

installError

這就尷尬了,于是一陣瘋狂的搜索 google stackoverflow 等,然而并沒有什么卵用,而且上面催得急,根本沒時間處理這些小問題 業(yè)務(wù)重構(gòu)是最主要的,以至于我們的業(yè)務(wù)組件沒有做到獨立倉庫拆分。
直到最近終于找到了解決辦法:( 主要是自己的功力不夠深厚

理論功底

動態(tài)庫和靜態(tài)庫

介紹

首先靜態(tài)庫和動態(tài)庫都是以二進制提供代碼復(fù)用的代碼庫

  • 靜態(tài)庫 常見的是 .a
  • 動態(tài)庫常見的是 .dll(windows) .dylib(mac) so(linux)
  • framework(in Apple): Framework是Cocoa/Cocoa Touch程序中使用的一種資源打包方式,可以將代碼文件、頭文件、資源文件、說明文檔等集中在一起,方便開發(fā)者使用。
    也就是說我們的framework其實是資源打包的方式,和靜態(tài)庫動態(tài)庫的本質(zhì)是沒有關(guān)系的

靜態(tài)庫和動態(tài)庫的區(qū)別

靜態(tài)庫: 鏈接時會被完整的復(fù)制到可執(zhí)行文件中,所以如果兩個程序都用了某個靜態(tài)庫,那么每個二進制可執(zhí)行文件里面其實都含有這份靜態(tài)庫的代碼
動態(tài)庫: 鏈接時不復(fù)制,在程序啟動后用dyld加載,然后再決議符號,所以理論上動態(tài)庫只用存在一份,好多個程序都可以動態(tài)鏈接到這個動態(tài)庫上面,達到了節(jié)省內(nèi)存(不是磁盤是內(nèi)存中只有一份動態(tài)庫),還有另外一個好處,由于動態(tài)庫并不綁定到可執(zhí)行程序上,所以我們想升級這個動態(tài)庫就很容易,windows 和linux上面一般插件和模塊機制都是這樣實現(xiàn)的。

But我們的蘋果爸爸在iOS平臺上規(guī)定不允許存在動態(tài)庫,并且所有的IPA都需要經(jīng)過蘋果爸爸的私鑰加密后才能用,基本你用了動態(tài)庫也會因為簽名不對無法加載,(越獄和非APP store除外)。于是就把開發(fā)者自己開發(fā)動態(tài)庫掐死在幻想中。
直到有一天,蘋果爸爸的iOS升級到了8,iOS出現(xiàn)了APP Extensionswift編程語言也誕生了,由于iOS 主APP需要和Extension共享代碼,Swift語言的機制也只能有動態(tài)庫,于是蘋果爸爸尷尬了,不過這難不倒我們的蘋果爸爸,畢竟我是爸爸,規(guī)則是我來定,我想怎樣就怎樣,于是提出了一個概念Embedded Framework,這種動態(tài)庫允許APPAPP Extension共享代碼,但是這份動態(tài)庫的生命被限定在一個APP進程內(nèi)。簡單點可以理解為 被閹割的動態(tài)庫。

舉個例子, iOS項目中使用Embeded Framework

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

靜態(tài)庫和動態(tài)庫如何構(gòu)建和加載

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


上面的介紹貌似有點抽象啊 套用在美團技術(shù)分享大會上的話就是:
靜態(tài)庫: 一堆目標(biāo)文件(.o/.obj)的打包體(并非二進制文件)
動態(tài)庫: 一個沒有main函數(shù)的可執(zhí)行文件


這里我們來復(fù)習(xí)下C語言的基本功,編譯和鏈接
編譯:將我們的源代碼文件編譯為目標(biāo)文件
鏈接:將我們的各種目標(biāo)文件加上一些第三方庫,和系統(tǒng)庫鏈接為可執(zhí)行文件。
由于某個目標(biāo)文件的符號(可以理解為變量,函數(shù)等)可能來自其他目標(biāo)文件,其實鏈接這一步最主要的操作就是 決議符號的地址。

  • 若符號來?靜態(tài)庫(本質(zhì)就是.o的集合包)或 .o,將其納?鏈接產(chǎn)物,并確定符號地址
  • 若符號來?動態(tài)庫,打個標(biāo)記,等啟動的時候再說---交給dyld去加載和鏈接符號

于是鏈接加裝載就有了不同的情況

  1. Load 裝載:將庫?件載?內(nèi)存
    • Static Loading:啟動時
    • Dynamic Loading:啟動后(使?時)
  2. Link 鏈接:決議符號地址
    • Static Linking:構(gòu)建(鏈接)時
    • Dynamic Linking:運?時(啟動時或使?時)

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

  1. Static Loading + Static Linking
  2. Static Loading + Dynamic Linking
  3. Dynamic Loading + Dynamic Linking
  4. Dynamic Loading + Static Linking
    第一種是純靜態(tài)庫相關(guān)了
    第二種就是靜態(tài)加載(啟動時),動態(tài)鏈接 ,鏈接時,動態(tài)庫參與鏈接,但是這時候只是給符號打了標(biāo)記告訴我這個符號來自與動態(tài)庫,程序啟動時,iOS或者Mac OS操作系統(tǒng)的dyld自動 load+link。
    既然全部都是自動的。那么符號的調(diào)用方完全不知道你到底是源碼還是靜態(tài)庫,動態(tài)庫 。
    第三種收到調(diào)用dlopen + performSelector 通常iOS的APP不適用這里不討論
    第四種,沒見過,個人也不是特別懂
    有需求請參看文后的程序員的自我修養(yǎng)一書

靜態(tài)庫和動態(tài)庫依賴關(guān)系

既然有2種庫,那么依賴關(guān)系又是2*2嘍

  1. libA.a dependency libB.a
  2. UIKit.dylib dependency Foundation.dylib
  3. libA.a dependency Foundation.dylib
  4. MyXX.dylib dependency libA.a

第一種 靜態(tài)庫互相依賴,這種情況非常常見,制作靜態(tài)庫的時候只需要有被依賴的靜態(tài)庫頭文件在就能編譯出來。但是這就意味者你要收到告訴使用者你的依賴關(guān)系
幸運的是 CocoaPod就是這樣做的
第二種動態(tài)庫依賴動態(tài)庫,兩個動態(tài)庫是相互隔離的具有隔離性,但是制作的靜態(tài)庫的時候需要被依賴動態(tài)庫參與鏈接,但是具體的符號決議交給dyld來做。
第三種,靜態(tài)庫依賴動態(tài)庫,也很常見,靜態(tài)庫制作的時候也需要動態(tài)庫參與鏈接,但是符號的決議交給dyld來做。
第四種,動態(tài)庫依賴靜態(tài)庫,這種情況就有點特殊了。首先我們設(shè)想動態(tài)庫編譯的時候需要靜態(tài)庫參與編譯,但是靜態(tài)庫交由dyld來做符號決議,but 這和我們前面說的就矛盾了啊。靜態(tài)庫本質(zhì)是一堆.o的打包體,首先并不是二進制可執(zhí)行文件,再者你無法保證主程序把靜態(tài)庫參與鏈接共同生成二進制可執(zhí)行文件。這就尷尬了。
怎么辦?
目前的編譯器的解決辦法是,首先我無法保證主程序是否包含靜態(tài)庫,再者靜態(tài)庫也無法被dyld加載,那么我直接把你靜態(tài)庫的.o偷過來,共同組成一個新的二進制。也被稱做吸附性

那么我有多份動態(tài)庫都依賴同樣的靜態(tài)庫,這就尷尬了,每個動態(tài)庫為了保證自己的正確性會把靜態(tài)庫吸附進來。然后兩個庫包含了同樣的靜態(tài)庫,于是問題就出現(xiàn)了。 看到這里想必前面出現(xiàn)的錯誤你已經(jīng)能猜出來了把_

后面再詳細解釋

先來個總結(jié)
可執(zhí)??件(主程序或者動態(tài)庫)在構(gòu)建的鏈接階段

  • 遇到靜態(tài)庫,吸附進來
  • 遇到動態(tài)庫,打標(biāo)記,彼此保持獨?

Xcode 項目結(jié)構(gòu)

target-對于一個產(chǎn)物(app,.a ,.framework)
project-一個項目包含多個target
workspace:一個包含多個target
schema: 指定了一個產(chǎn)物是按照何種的依賴關(guān)系,編譯-鏈接到最終的一個產(chǎn)物

iOS依賴管理事實上的標(biāo)準(zhǔn)

這么多年,Apple的博客和文檔也就告訴了我們什么是靜態(tài)庫 什么是動態(tài)庫,如何制作等。但是并沒有給我們提供一系列的依賴管理工具。所以CocoaPods成了事實上的標(biāo)準(zhǔn)。
通常CocoaPods管理的工程結(jié)構(gòu)如下:

? CocoaPods
      + App.workspace
            + App.project
            ? Pods.project
                    ? pod target => .a

那么當(dāng)我們按下CMD+B的時候,整個項目按照先編譯被依賴Pod,然后依賴其他Pod的Pod也被構(gòu)建出來,最終所有的組件被編譯為一個lib-Pods-XXXAPP.a被添加進項目進去。資源通過CocoaPods提供的腳本也一并被復(fù)制進去。想了解CocoaPods做了什么的讀者可以參看后面的鏈接

解決問題

這么多理論功底的建立,相信我們已經(jīng)能分析出來之前pod install的原因了。就是用了use_framework那么我們的所有Pod都會以動態(tài)庫(Embeded Framework)的形式去構(gòu)建,于是那些非開源的庫(如 百度地圖,微信分享)如果被多個Pod依賴(組件化開發(fā)中太常見了)于是被吸附到動態(tài)庫里面,所以CocoaPod直接就不讓我們install成功。因為你現(xiàn)在的依賴管理就是錯誤的。
在聽取美團葉樉老師分享的時候 他們的出發(fā)點是因為要繞過蘋果爸爸在iOS9以下對__text段60M的限制使用了動態(tài)庫方案,我們是因為某些swift庫必須要用到(歷史遺留原因)動態(tài)庫。美團的做法是摘除依賴關(guān)系,自定義CocoaPods(開源的本來就是用著不爽我就改)。但是我是個小菜雞啊。我也不會ruby(以后會學(xué)的),但是葉樉老師給我提了別的idea。 前面我們知道 動態(tài)庫和動態(tài)庫是隔離性,動態(tài)庫依賴靜態(tài)庫具有吸附性,那么我們可以自定義一個動態(tài)庫把百度地圖這種靜態(tài)庫吸附進來。對外整體呈現(xiàn)的是動態(tài)庫特性。其他的組件依賴我們自定義的動態(tài)庫,由于隔離性的存在,不會出現(xiàn)問題。

制作動態(tài)庫

1 創(chuàng)建動態(tài)庫項目這里以wx舉例


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


3 將wx的PublicHeader暴露出來,注意由于我并沒有使用到wx相關(guān)API所以鏈接器幫我們鏈接動態(tài)庫 的時候可能并不會把wx靜態(tài)庫吸附進來。我們手動在build Setting的other link flags加上-all_load標(biāo)記

4.在Schema里面跳轉(zhuǎn)編譯配置為Release ,并且選擇所有的CPU架構(gòu)



5 然后選擇模擬器或者Generic iOS Device運行編譯就會生成對應(yīng)版本的Framework了。


6.但是為了保證開發(fā)者使用的時候是真機模擬器都能正常使用,我們需要合并不同架構(gòu)
這里在Build Phases里添加以下腳本,真機和模擬器都Build一遍之后就會在工程目錄下生成Products文件夾,

if [ "${ACTION}" = "build" ]
then
INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}.framework
DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework
SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
#ditto "${DEVICE_DIR}/Headers" "${INSTALL_DIR}/Headers"
lipo -create "${DEVICE_DIR}/${PROJECT_NAME}" "${SIMULATOR_DIR}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}"
open "${DEVICE_DIR}"
open "${SRCROOT}/Products"
fi

于是我們有了我們自己的私有動態(tài)庫LJWXSDK,那么我們來驗證我們之前的問題
首先指定一個LJWXSDK.podspec這里我直接傳到了我的Github上面

#
# Be sure to run `pod lib lint LJPod.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
#

Pod::Spec.new do |s|
  s.name             = 'LJWXSDK'
  s.version          = '0.1.0'
  s.summary          = 'A short description of LJWXSDK.'


  s.description      = <<-DESC
  TODO: Add long description of the pod here.
  DESC

  s.homepage         = 'https://github.com/ValiantCat/LJWXSDK'

  s.license          = { :type => 'MIT', :file => 'LICENSE' }
  s.author           = { 'ValiantCat' => '519224747@qq.com' }
  s.source = { :http  => 'http://onk2m6gtu.bkt.clouddn.com/LJWXSDK.framework.zip' }


  s.ios.deployment_target = '8.0'
  s.default_subspec = 'zip'

  s.subspec 'zip' do |zip|
  
    puts '-------------------------------------------------------------------'
    puts 'Notice:LJWXSDK is zip now'
    puts '-------------------------------------------------------------------'

    zip.ios.vendored_frameworks = '*.framework'
  end

end

注意上面我是把二進制壓縮丟進了七牛的oss文件存儲。畢竟免費還快。

然后通過pod lib create創(chuàng)建了一個pod用來驗證之前我們的傳遞性依賴問題,
文件夾結(jié)構(gòu)如下

.
├── Example
│   ├── LJA
│   │   ├── Base.lproj
│   │   │   ├── LaunchScreen.storyboard
│   │   │   └── Main.storyboard
│   │   ├── Images.xcassets
│   │   │   └── AppIcon.appiconset
│   │   │       └── Contents.json
│   │   ├── LJA-Info.plist
│   │   ├── LJA-Prefix.pch
│   │   ├── LJAppDelegate.h
│   │   ├── LJAppDelegate.m
│   │   ├── LJViewController.h
│   │   ├── LJViewController.m
│   │   ├── en.lproj
│   │   │   └── InfoPlist.strings
│   │   └── main.m
│   ├── LJA.xcodeproj
│   ├── LJA.xcworkspace
│   ├── Podfile
│   ├── Podfile.lock
│   ├── Pods
│   │   ├── Headers
│   │   ├── LJWXSDK
│   │   │   └── LJWXSDK.framework
│   │   │       ├── Headers
│   │   │       │   ├── LJWXSDK.h
│   │   │       │   ├── WXApi.h
│   │   │       │   ├── WXApiObject.h
│   │   │       │   └── WechatAuthSDK.h
│   │   │       ├── Info.plist
│   │   │       ├── LJWXSDK
│   │   │       ├── Modules
│   │   │       │   └── module.modulemap
│   │   │       ├── _CodeSignature
│   │   │       │   └── CodeResources
│   │   │       └── read_me.txt
│   │   ├── Local\ Podspecs
│   │   │   ├── LJA.podspec.json
│   │   │   ├── LJB.podspec.json
│   │   │   └── LJWXSDK.podspec.json
│   │   ├── Manifest.lock
│   │   ├── Pods.xcodeproj
│   │   │   ├── project.pbxproj
│   │   │   ├── project.xcworkspace
│   │   ├── Target\ Support\ Files
│   │   │   ├── LJA
│   │   │   │   ├── Info.plist
│   │   │   │   ├── LJA-dummy.m
│   │   │   │   ├── LJA-prefix.pch
│   │   │   │   ├── LJA-umbrella.h
│   │   │   │   ├── LJA.modulemap
│   │   │   │   └── LJA.xcconfig
│   │   │   ├── LJB
│   │   │   │   ├── Info.plist
│   │   │   │   ├── LJB-dummy.m
│   │   │   │   ├── LJB-prefix.pch
│   │   │   │   ├── LJB-umbrella.h
│   │   │   │   ├── LJB.modulemap
│   │   │   │   └── LJB.xcconfig
│   │   │   ├── Pods-LJA_Example
│   │   │   │   ├── Info.plist
│   │   │   │   ├── Pods-LJA_Example-acknowledgements.markdown
│   │   │   │   ├── Pods-LJA_Example-acknowledgements.plist
│   │   │   │   ├── Pods-LJA_Example-dummy.m
│   │   │   │   ├── Pods-LJA_Example-frameworks.sh
│   │   │   │   ├── Pods-LJA_Example-resources.sh
│   │   │   │   ├── Pods-LJA_Example-umbrella.h
│   │   │   │   ├── Pods-LJA_Example.debug.xcconfig
│   │   │   │   ├── Pods-LJA_Example.modulemap
│   │   │   │   └── Pods-LJA_Example.release.xcconfig
│   │   │   └── Pods-LJA_Tests
│   │   │       ├── Info.plist
│   │   │       ├── Pods-LJA_Tests-acknowledgements.markdown
│   │   │       ├── Pods-LJA_Tests-acknowledgements.plist
│   │   │       ├── Pods-LJA_Tests-dummy.m
│   │   │       ├── Pods-LJA_Tests-frameworks.sh
│   │   │       ├── Pods-LJA_Tests-resources.sh
│   │   │       ├── Pods-LJA_Tests-umbrella.h
│   │   │       ├── Pods-LJA_Tests.debug.xcconfig
│   │   │       ├── Pods-LJA_Tests.modulemap
│   │   │       └── Pods-LJA_Tests.release.xcconfig
│   │   └── libWeChatSDK
│   │       ├── README.md
│   │       ├── WXApi.h
│   │       ├── WXApiObject.h
│   │       ├── WechatAuthSDK.h
│   │       └── libWeChatSDK.a
├── LICENSE
├── LJA
│   ├── Assets
│   └── Classes
│       └── LJA.m
├── LJA.podspec
├── LJB
│   ├── Assets
│   └── Classes
│       └── LJB.m
├── LJB.podspec
├── README.md
└── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj


測試工程我也丟在7牛上面。下載測試即可
編譯運行。完美。我們又可以愉快的和swift第三方庫配合使用。
很多人可能會問 諸如百度地圖 微信這種sdk為什么官方不支持動態(tài)庫版(所說的都是embeded Framework),猜測是為了兼容更低iOS7版本吧
很多人會覺得麻煩的要死。首先每個公司多多少少都有歷史包袱,麻煩也要做,再者這是一次對基本功的補充,即便你們沒有用到,但是為了學(xué)習(xí),這篇教程所做的也值得你嘗試一次。

剖析下動態(tài)庫Framework吧

上述解決了我們一開始遇到的問題。but既然動態(tài)庫和靜態(tài)庫壓根就不一回事,所以里面還是有很多細節(jié)值得我們?nèi)チ私獾摹?/p>

回過頭來看Embened Framework

首先我們之前記得如果一個動態(tài)庫加在LinkedFrameworksand Libraies程序啟動就會報ImageNotFound,如果放在EmbededBinaries里面就可以。這是為什么呢。我們拿MacoView來看下兩種情況下可執(zhí)行文件的細節(jié)

其中@rpth這個路徑表示的位置可以查看Xcode 中的鏈接路徑問題
這樣我們就知道了其實加在EmbededBinaries里面的東西其實會被復(fù)制一份到xx.app里面,所以這個名字起得還是不錯的直譯就是嵌入的框架

Why Swift does not Support Staic Libraies

造成這個的主要原因是Swift的運行時庫(不等同于OC的runtime概念),由于Swift的ABI不穩(wěn)定,靜態(tài)庫會導(dǎo)致最終的目標(biāo)程序中包含重復(fù)的運行庫,相關(guān)可以看下最后的參考文章SwiftInFlux#static-libraries。等到我們的SwiftABI穩(wěn)定之后,我們的靜態(tài)庫支持可能就又會出現(xiàn)了。當(dāng)然也可能不出,Swift伴隨誕生的SPM(Swift,Package Manager),可能有更好的官方的包依賴管理工具。讓我們期待吧。

CocoaPods 使用Use_framework!

既然加了Swift的第三方庫之后就需要在Podfile里面加上use_framework! 那么CocoaPods就會幫我們生成動態(tài)庫,但是奇怪的是,我們并沒有在主工程的embeded binaries看到這個動態(tài)庫,這又是什么鬼。其實是CocoaPods使用腳本幫我們加進去了。腳本位置在主工程的 build Phase下的 Emded Pods frameworks

"${SRCROOT}/Pods/Target Support Files/Pods-LJA_Example/Pods-LJA_Example-frameworks.sh"

動態(tài)庫Framework的文件結(jié)構(gòu)

.
├── Headers
│   ├── LJWXSDK.h
│   ├── WXApi.h
│   ├── WXApiObject.h
│   └── WechatAuthSDK.h
├── Info.plist
├── LJWXSDK
├── Modules
│   └── module.modulemap
└── _CodeSignature
    └── CodeResources


  1. Headers 一般是頭文件。非private里面的頭文件都會在里面
  2. info.plist 配置信息,不深究
  3. Modules 這個文件夾里有個module.modulemap文件,后面在講解
  4. 二進制文件,這就是上面提到的不帶 main的二進制文件了,.o的打包體
  5. _codeSignature 簽名文件 (蘋果爸爸的約束)
  6. more 資源文件。這里暫時沒用到,所以沒有 ,但是這個也是個大坑

更愉快的導(dǎo)入文件

@class,@protocol 不說了就是聲明一個類,并不導(dǎo)入。
#import <>, #import""是加強版的#include<>,#include"" 防止重復(fù)導(dǎo)入的。
#import<> : 通過 build setting里面中的 header Search Path里面去找
#import"" : 第一步先搜索user Header search Path 再搜索 header search Path 。所以對我們的framework來說,CocoaPod 幫我們加到了 Header search Path 目前2種導(dǎo)入方式都是可以支持的。
上面的導(dǎo)入方式都帶了 某個framework的路徑 <XX/xx.h> "xx/xx.h" ,我們在開發(fā)自己主工程的時候會發(fā)現(xiàn)我們導(dǎo)入主工程其他類是不需要導(dǎo)入前綴的。 這又是怎么回事。
看下面的配置

目前的配置是non-recursive。如果把non去掉意思就是我可以遞歸的去查找某些framework下面的頭文件了。 但是Xcode的效率肯定就會有影響。
還是不建議修改的好。


大家都知道iOS7之后多了@import,這又是什么鬼。
簡單理解這個方式叫做Module導(dǎo)入,好處就是使用了@import之后不需要在project setting手動添加 framework,系統(tǒng)會自動加載,而且效率更高。
最主要的是swift也只能這樣用。
導(dǎo)入的時候系統(tǒng)會查找如果有模塊同名的文件就會導(dǎo)入這個文件。如果沒有CocoaPods幫我們生成一個module-umbrela.hl文件,然后就是導(dǎo)入的這個文件。

回過頭來看我們的framework的結(jié)構(gòu) 里面有個Modules文件夾,里面有個文件module.modulemap

framework module LJWXSDK {
  umbrella header "LJWXSDK.h"

  export *
  module * { export * }
}

我們可以看到其實被暴露的header就是這個文件,之前我在按照#import "/"的時候有個警告


而且按照@import導(dǎo)入的東西發(fā)現(xiàn)沒有導(dǎo)入可用的頭文件就是因為并沒有在 umbrella header的頭文件中加入其他頭文件。
加入之后我們就可以完美的使用@import ,并且#import"/" 也不會報warning
更多關(guān)于umbrella Header 參看文后參考

資源問題

首先我們來看常見的資源文件:主要分為圖片和其他類資源那么加載圖片和加載其他資源都是怎么做的?
1: [UIimage imageNamed:]
2: [NSbundle bundleForclass[XXX class]]
其實方式1去本質(zhì)就是去mainBundle去拿資源,方式2從XXX所在的框架里面去拿。
前面也說道framework只是資源的打包方式,本質(zhì)上是有兩種的。
我們這個framework如果本質(zhì)是靜態(tài)庫,那么無需改變使用方式,資源最終都會打包到Main Bundle里面
如果我們這個framework本質(zhì)是動態(tài)庫,那么我們的資源就發(fā)生了變化,資源就會被存放在 framework里面。所以我們需要使[NSbundle bundleForclass[XXX class]]。需要注意的是很多人為了簡單,下意識的使用self class 傳遞,但是有可能這個self實例不在資源所屬的framework。所以會出現(xiàn)資源加載 失敗。一定要謹(jǐn)慎使用。

參考

程序員的自我修養(yǎng),鏈接,裝載 和庫
iOS里的動態(tài)庫和靜態(tài)庫
Systems Programming: What is the exact difference between Dynamic loading and dynamic linking?
CocoaPods 都做了什么?
Dynamic Linking of Imported Functions in Mach-O
OS里的導(dǎo)入頭文件
iOS - Umbrella Header在framework中的應(yīng)用
SwiftInFlux#static-libraries
iOS里的導(dǎo)入頭文件
iOS - Umbrella Header在framework中的應(yīng)用
@import vs #import - iOS 7

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內(nèi)容