1.什么是庫,為什么使用庫?
庫是共享程序代碼的方式,一般分為靜態庫和動態庫;庫實現了iOS程序的模塊化,將某些特定的功能模塊化為庫的格式方便分享和使用!
2.靜態庫和動態庫有什么特點?
異同點:
靜態庫:鏈接時完整地拷貝至可執行文件中,被多次使用就有多份冗余拷貝。
動態庫:鏈接時不復制,程序運行時由系統動態加載到內存,供程序調用,系統只加載一次,多個程序可以共用,節省內存。
共同點:
靜態庫和動態庫都是閉源庫,只能拿來滿足某個功能的使用,不會暴露內部具體的代碼信息,而從github上下載的第三方庫大多是開源庫
3.這兩種庫都有哪些文件格式?
靜態庫:.a和.framework
動態庫:.dylib和.framework(系統直接提供給我們的framework都是動態庫!)
注意:1.兩種庫都有framework的格式,但是它們長得一樣嗎?
2.當你創建一個framework文件時,系統“默認”是一個動態庫的格式,如果想做成靜態庫,需要在buildSetting中將Mach-O Type選項設置為Static Library就行了!
4. .a文件和.framework文件組成的區別:
.a文件是一個純二進制文件,不能直接拿來使用,需要配合頭文件、資源文件一起使用。
將靜態庫打包的時候,只能打包代碼資源,但是圖片文件、本地json文件和xib等資源文件無法打包進去,使用.a靜態庫的時候需要三個組成部分:.a文件+需要暴露的頭文件+資源文件;
.framework文件內部除了有二進制文件(如下圖黑色文件)之外還有其他的資源文件(相當于:.framwork文件=黑色二進制文件<.a文件+.h文件>+資源文件<圖片、以及本地的html5,json,plist等),可以直接拿來在工程中使用。
5.制作靜態庫時需要注意的幾點:
(1)圖片資源的處理:兩種格式的靜態庫,一般都是把圖片文件單獨的放在一個.bundle文件中,一般.bundle的名字和.a或.framework的名字相同。(.bundle文件很好弄,在桌面上新建一個文件夾,把它重命名為XXX.bundle就可以了(選中文件->右鍵->顯示包內容->拖拽添加圖片資源))。
(2)category是我們實際開發項目中經常用到的,把category打成靜態庫是沒有問題的,但是在使用這個靜態庫的工程中,調用category中的方法時,會出現找不到該方法的運行時錯誤:selector not recognized,解決辦法是:在使用靜態庫的工程中配置other linkerflags的值為-ObjC。???????????????????????????????????????????????????????????????????????????????
(3)如果一個靜態庫很復雜,需要暴露的.h比較多的話,就可以在靜態庫的內部創建一個.h文件(一般這個.h文件的名字和靜態庫的名字相同),然后把所有需要暴露出來的.h文件都集中放在這個.h文件中,而那些原本需要暴露的.h都不需要再暴露了,只需要把.h暴露出來就可以了。
6.framework動態庫的主要作用:
framework本來是蘋果專屬的內部提供的動態庫文件格式,但是自從2014年WWDC之后,開發者也可以自定義創建framework實現動態更新(繞過apple store審核,從服務器發布更新版本)的功能,這與蘋果限定的上架的app必須經過apple store的審核制度是沖突的,所以含有自定義的framework的app是無法在商店上架的,但是如果開發的是企業內部應用,就可以考慮嘗試使用動態更新技術來將多個獨立的app或者功能模塊集成在一個app上面!(我開發的就是企業內部使用的app,我們將企業官網中的板塊開發成4個獨立的app,然后將其改造為framework文件最終集成在一款平臺級的app當中進行使用,這樣就可以在一款app上面使用原本4個app的全部功能!)
7.iOS 如何使用 framework 來進行動態更新!
重要參考文檔(一定要看):
http://blog.csdn.net/like7xiaoben/article/details/44081257
http://www.tuicool.com/articles/Ybq6Rf3
?(1)常見問題匯總:
NO.1:重點是利用OC的動態特性去實現framework的加載啟動!
注意:普通的alloc init的方法無法得到動態庫中的類,必須依靠NSClassFromString
和performSelector的動態特性去程序內部查找動態庫的類并調用啟動方法!
NO.2:當framework 和 主工程中引用相同的第三方庫時:
在主工程中啟動動態庫的時候,打印臺會顯示“ClassXXX is implemented in both XXXandXXX.One of the two will be used.Which one is undefined.”
這是當 framework 工程和 主工程鏈接了相同的第三方庫或者同名的類造成的。但是可能并不影響程序的正常運行,一般的做法是在framework 的Build Settings > Other Linker Flags添加-undefined dynamic_lookup標記即可!這樣操作的思想就是確保framework也從主工程中鏈接第三方庫進行使用,但是可能會存在一些隱患問題,比如:動態庫和主工程中都使用AFNetworking進行網絡請求,但是定義的http請求的方法可能不同,如果讓動態庫調用主工程方法就需要確保主工程中的方法能夠滿足動態庫內部程序的使用,如果打印臺出現了上述信息卻不影響程序正常運行的時候,建議先不要做過多操作,而是查看動態庫啟動時選擇加載的是哪里的第三方庫(經我測試,framework會加載自己工程里面的AFNetworking方法而不是主工程中的方法),然后再進行修改。
N0.3:出現“mmap() error 1 at address=0x100704000, size=0x00008000 segment=__TEXT”錯誤是什么原因?
這個問題主要是因為動態庫打包時使用的證書和主程序調試證書不一致造成的,證書不一致時,主程序中是無法打開動態庫的!!
NO.4:framework和主工程使用同名的類文件出現的錯誤:
嘗試過在 framework 中引用主工程中已有的文件,通過Build Settings > Header Search Paths中添加相應的目錄,Xcode 在編譯的時候可以成功(因為添加了-undefined dynamic_lookup),并且 Debug 版本是可以正常運行的,但是 Release 版本動態加載時會提示找不到符號:“Error Domain=NSCocoaErrorDomain Code=3588"The bundle “YourFramework” couldn’t be loaded."(dlopen(/var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework”
這是因為 Debug 版本暴露了所有自定義類的符號以便于調試,因此你的 framework 可以找到相應的符號,而 Release 版本則不會。目前能想到的方法只有將相同的文件拷貝一份到 framework 工程里,并且更改類名。這一點,只要在平時開發中養成定義類名時添加前綴的習慣基本就能避免。
NO.5:怎么訪問 framework 中的資源文件?
在framework中使用 storyboard/xib創建的頁面,可以直接訪問framework中圖片資源!
但是framework中通過"imageNamed:"方式加載的照片都會丟失!!!這是因為imageNamed的方法默認是從mainBundle中查找資源的,而framework中的照片是從framework內部加載的,這是的bundle并不是mainBudle,而是存在于主程序的docment文件中的framework包,圖片加載的路徑發生了變化,自然找不到圖片資源,所以需要修改加載圖片的方法!(這是一個很蛋疼的問題,意味著原先工程中加載圖片的imageNamed的方法都需要更改為從指定bundle加載圖片資源的方法。我是在framework中寫了一個UIImage的category方法"imageFromBundleNamed"替換掉framework中所有的“imageNamed”的方法才實現圖片資源的加載)
NO.6主工程中加載framework可能出現彈窗顯示位置失常!
這個問題是很容易出現的,因為在主工程中打開framework就向從一款App跳轉到另一款App,framwork中彈出的窗口并不受主工程的navigationController的管理,這樣就極可能導致彈窗展示位置出錯。
解決辦法是:1.將framework中彈窗加載的keyWindow更換為lastWindow(keyWindow是當前活躍的window,而lastWindow是最頂部的window);2.在framwork中通過navigationController.view獲取彈窗展示的當前View(此時的View也必然是framework中最前端的頁面),然后做相應操作!
NO.7在framework中,圖片資源不能使用xcassets進行管理!
這是因為framework中的xcassets文件打包之后會變成.cer文件,并不暴露出圖片資源,所以framework不能通過xcassets來管理圖片資源。
解決辦法是:創建一個資源文件夾把全部圖片文件都裝進去進行管理。
NO.8圖片命名問題:
同一個圖片包含多種適配尺寸的,必須確保圖片名一致,且以 @2x/@3x 結尾,大小寫敏感。當我們調用圖片的時候只使用@符號以前的圖片名即可,因為系統會根據手機屏幕的不同自動選擇加載該圖片名下的@2x或@3x的圖片!
NO.9架構錯誤:
錯誤描述:“dlopen(/path/to/framework,9): no suitable image found.? Didfind:/path/to/framework:mach-o, but wrong architecture”
解決辦法:打包framework的時候一定要分清是支持iphone還是iphonesimulayor,如果打包類型有誤就不能在相應設備上正常啟動framework文件;當然,最好是通過lipo的合并命令講真機包和模擬器包合并一下,這樣就不用擔心引起架構錯誤的問題!
(1)通過終端命令查看framework包支持的架構信息:lipo -info (把framework中的黑色文件拖進來)可以查看framework的架構信息!我們打包的framework會有四種類型:
(2)合并命令:lipo -create?? xxx? xxx -output newName
“xxx”是:將模擬器包和真機包的黑色文件拖拽到終端中時產生的路徑信息;
“newName”是合并后自定義的名字,不過我一般是再拖一遍真機包的路徑來替換這個名字,這樣合并后的文件就直接包含在真機包里面,然后拿來使用。
N0.10簽名問題:
系統在加載動態庫時,會檢查 framework 的簽名,簽名中必須包含 TeamIdentifier 并且 framework 和? 主工程 的 TeamIdentifier 必須一致。
如果不一致,否則會報下面的"mmap()"錯誤:
Error loading/path/to/framework: dlopen(/path/to/framework,265): no suitable image found. Didfind:/path/to/framework:mmap() error1
NO.11:關于other linker flag的設置問題:
使用動態庫的時候極易發生鏈接錯誤,而且大多發生在加載framework中的category的情況!根本原因在于Objective-C的鏈接器并不會為每個方法建立符號表,而是僅僅為類建立了符號表。這樣的話,如果靜態庫中定義了已存在的一個類的分類,鏈接器就會以為這個類已經存在,不會把分類和核心類的代碼合起來。這樣的話,在最后的可執行文件中,就會缺少分類里的代碼,這樣函數調用就失敗了。常見的設置方法就是在other linker flag中添加一個語句:-all_load,但是這樣也并不是萬能的,只是在一定程度上解決了鏈接問題。
關于flag標注信息:-all load和-Objc的區別請參考鏈接:http://my.oschina.net/u/728866/blog/194741
注意:當flag里面添加了注釋卻還是無法使用的時候,可能報flag與bitcode沖突的問題尤其是第三方庫可能和bitcode沖突),這樣的話就需要將bitcode設置為NO!
bitcode的具體作用不做詳談,可參考:http://www.lxweimin.com/p/3e1b4e2d06c6
以下的這些問題是我拷貝原文資料的信息,我在測試中并未遇到相關錯誤,權作參考:
N0.12:如果用來打包的證書是 iOS 8 發布之前生成的,則打出的包驗證的時候會沒有 TeamIdentifier 這一項。這時在加載 framework 的時候會報下面的錯誤:
[deny-mmap]mappedfilehasno team identifierandisnotaplatformbinary:/private/var/mobile/Containers/Bundle/Application/5D8FB2F7-1083-4564-94B2-0CB7DC75C9D1/YourAppNameHere.app/Frameworks/YourFramework.framework/YourFramework
可以通過codesign命令來驗證。
codesign -dv /path/to/YourApp.app
如果證書太舊,輸出的結果如下:
Executable=/path/to/YourApp.app/YourAppIdentifier=com.company.yourappFormat=bundlewithMach-O thin (armv7)CodeDirectoryv=20100size=221748flags=0x0(none)hashes=11079+5location=embeddedSignaturesize=4321SignedTime=2015年10月21日 上午10:18:37Info.plistentries=42TeamIdentifier=notsetSealed Resourcesversion=2rules=12files=2451Internal requirementscount=1size=188
注意其中的TeamIdentifier=not set。
采用 swift 加載 libswiftCore.dylib 這個動態庫的時候也會遇到這個問題,對此Apple 官方的解釋是:
To correct this problem, you will need to sign your app using code
signing certificates with the Subject Organizational Unit (OU) set to
your Team ID. All Enterprise and standard iOS developer certificates
that are created after iOS 8 was released have the new Team ID field in
the proper place to allow Swift language apps to run.
If you are an in-house Enterprise developer you will need to be
careful that you do not revoke a distribution certificate that was used
to sign an app any one of your Enterprise employees is still using as
any apps that were signed with that enterprise distribution certificate
will stop working immediately.
只能通過重新生成證書來解決這個問題。但是 revoke 舊的證書會使所有用戶已經安裝的,用該證書打包的 app 無法運行。
等等,我們就跪在這里了嗎?!
現在企業證書的有效期是三年,當證書過期時,其打包的應用就不能運行,那企業應用怎么來更替證書呢?
Apple 為每個賬號提供了兩個證書,這兩個證書可以同時生效,這樣在正在使用的證書過期之前,可以使用另外一個證書打包發布,讓用戶升級到新版本。
也就是說,可以使用另外一個證書來打包應用,并且可以覆蓋安裝使用舊證書打包的應用。詳情可以看Apple 文檔。