問題的提出:
隨着項目的愈來愈大,可能會出現好幾個團隊共同維護一個項目的狀況,好比:項目組A負責當中的A塊,項目組B負責當中的B塊.....這幾塊彼此之間既獨立,也相互聯繫.對於這樣的狀況,可以採用約定的方式,比方,你僅僅改動你那塊,不要改動公共的.假設要改動公共的,那麼必須要通知一下其餘組,你們共同決定怎麼改動,這樣的方式有一個很是大的問題,很是有可能不經意間就改了別的團隊的代碼.還有維護的愈來愈複雜等.
諸如此類的問題,很是天然的咱們會考慮每個團隊創建一個靜態庫
IOS的靜態庫有兩種,.a和.framework, 動態庫.framework居多. 靜態庫建議也生成.framework模式的,優勢是.framework包括了相關的.h文件等..a文件還要本身加入相關的.h文件
靜態庫:連接時,靜態庫會被完整地拷貝到可運行文件裏。被屢次使用就有多份冗餘拷貝.好比咱們在咱們的程序中使用了百度地圖的.a文件,另一個應用也使用了百度地圖.a.這樣整個系統中,會出現兩個 百度地圖.a文件
動態庫:連接時不復制,程序執行時由系統動態載入到內存,供程序調用,系統僅僅載入一次,多個程序共用。節省內存.系統提供的庫基本上都屬於此類,好比UIKit等,A應用和B應用中都用到了UIKit,系統僅僅載入了一份在內存中.IOS中對於第三方的動態庫,很是遺憾,眼下還不支持的.
一:framework的製做
從xcode6開始,已經提供了製做.framework的選項了
如下咱們用系統自帶的來新建.framework.而後再加入一個依賴庫 AFNetworking
1) xcode的菜單條 File -> New Project 創建一個名爲 YohunlUtilsLib 的Cocoa Touch Framework的project. 將Target 改成 7.0(由於現在大多數應用都還要支持7.0sdk,故此處咱們改動爲支持7.0)
這時候,編譯一下,你可能會遇到警告
(null): Embedded dylibs/frameworks only run on iOS 8 or later
彆着急,這個是由於 從IOS8開始,創建的framework能夠是動態的(並不是全然意義上的動態,這裏的動態,事實上仍是要嵌入到咱們的應用中,而不能夠從網上下載,而後用dlopen動態載入的).
咱們要改動 Mach-O Type.從默認的Dynamic Library 改成 Static Library,再編譯,警告消失
那麼選擇 Dynamic Library 仍是 Static Library 有什麼影響呢? 我已經實際的檢驗過了.(我採用了一個包括了 第三方庫 AFnetworking的測試project)
Static Library 支持IOS8.0 sdk下面的,從眼下來看,大多數project都還要支持7.0 sdk,因此咱們最常用的就是Static Library
當咱們使用 Static Library時候,生成的framework直接如同加入普通文件的形式加入進來就可以了.它會本身主動在
只是當咱們執行的時候,會提示:
當生成後,加入到演示樣例project中,
好吧,這樣的模式的加入,看來有些問題呀,還要本身加入依賴庫,這些依賴庫都是系統的,依據提示本身加入就可以了
再執行,OK
當咱們使用 Dynamic Library模式(
僅僅有IOS8.0及以上才支持)的時候.如同普通文件同樣加入到演示樣例project中去的話,執行,會出現提示:
dyld: Library not loaded: @rpath/NetTestLib.framework/NetTestLib
Referenced from: /Users/yohunl/Library/Developer/CoreSimulator/Devices/B1DBCA26-C113-4C74-BB81-297D4AF1E0C8/data/Containers/Bundle/Application/C1B4F6D5-96FD-4245-8E6D-4F1C569EEF6F/TestFramework.app/TestFramework
Reason: image not found
解決的方式
在project的配置 Embedded Binaries如下加入.而不是在 Linked Frameworks and Libraries下加入.這樣的方式,不需要加入 AFNetworking所需要的系統庫,這一點不知道是什麼緣由!!!假設有知道緣由的,請也告知我一下啊
再執行,OK
2) 命令行,進入文件夾下 pod init 創建一個podfile文件,因爲咱們製做的.framework需要用到AFNetworking網絡庫 PS:你也可以用隨意的方式創建這個podfile文件
打開它,因爲我採用 pod init命令創建的,打開後你會發現它已經有內容了
改動其內容爲
platform :ios, '7.0'
inhibit_all_warnings!
target 'YohunlUtilsLib' do
pod 'AFNetworking'
end
target 'YohunlUtilsLibTests' do
end
再運行 pod install 創建依賴
打開project,加入一個文件 YONetwork.h,在當中加入方法
@interface
YONetwork :
NSObject
/**
*
獲取
github
上用戶的
repo
*
*
@param
user
username
*
@param
success
成功的回調
*
@param
failure
失敗的回調
*/
- (
void
)getGithubReposForUser:(
NSString
*)user withSuccess:(
void
(^)(
id
responseObject))success failure:(
void
(^)(
NSError
*error))failure;
/**
*
測試
getGithubReposForUser:withSuccess:failure
的
*/
- (
void
)test;
@end
#import
"YONetwork.h"
#import
"AFNetworking.h"
@implementation
YONetwork
- (
void
)getGithubReposForUser:(
NSString
*)user withSuccess:(
void
(^)(
id
responseObject))success failure:(
void
(^)(
NSError
*error))failure
{
AFHTTPRequestOperationManager
*manager = [
AFHTTPRequestOperationManager
manager
];
[manager
GET
:[
NSString
stringWithFormat
:
@"https://api.github.com/users/%@/repos"
, user]
parameters
:
nil
success
:^(
AFHTTPRequestOperation
*operation,
id
responseObject) {
success(responseObject);
}
failure
:^(
AFHTTPRequestOperation
*operation,
NSError
*error) {
failure(error);
}];
}
- (
void
) test {
[
self
getGithubReposForUser
:
@"yohunl"
withSuccess
:^(
id
responseObject) {
NSLog
(
@"getGithubReposForUser response = %@"
,responseObject);
}
failure
:^(
NSError
*error) {
NSLog
(
@"getGithubReposForUser error = %@"
,error);
}];
}
@end
編譯執行 (command + B),生成對應的.framework文件
菜單 - organizer
可以看到文件夾結構例如如下:
當中的 YohunlUtilsLib.framework 就是咱們要的 :
注意,YohunlUtilsLib.framework中並無包括AFNetworking庫的內容,這一點咱們可以用例如如下的命令來驗證.
命令進入 Debug-iphoneos
$ lipo -info YohunlUtilsLib.framework/YohunlUtilsLib
輸出
Architectures in the fat file: YohunlUtilsLib.framework/YohunlUtilsLib are: armv7 arm64
說明咱們生成的真機framework:
YohunlUtilsLib.framework 包括了兩種架構
armv7 arm64
分離出每一種架構
$ mkdir armV7 當前文件夾創建文件夾,用於存放分離後的架構,和分離出的.o文件
$ lipo YohunlUtilsLib.framework/YohunlUtilsLib -thin armv7 -output ./armv7/YohunlUtilsLib_armv7 分離出armv7架構到目錄armV7下
$ cd armV7 進入目錄
$ ar -x YohunlUtilsLib_armv7 分離出armv7架構的所有的.o文件
分離後,如圖:
可以看到,最後的.framework中,並無包括咱們pod加入的 AFNetworking.
因此 當咱們把咱們生成的YohunlUtilsLib.framework給別人使用過的時候,別人本身要在項目中加入 AFNetworking,不然會提示找不到的錯誤.
3) 咱們使用命令來合併咱們生成的真機和模擬器的framework
創建一個新的target
target創建後
加入例如如下的合併模擬器和真機的腳本
# Constants
SF_TARGET_NAME=${PROJECT_NAME}
#
本身定義的用來存放最後合併的
framework
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
#IPHONE_DEVICE_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos
WORKSPACE_NAME=${PROJECT_NAME}.xcworkspace
YO_SCHEME=${PROJECT_NAME}
#clean build
是先清除原來的
build
xcodebuild -workspace ${WORKSPACE_NAME} -scheme ${YO_SCHEME} -sdk iphonesimulator -configuration
"${CONFIGURATION}"
clean build
xcodebuild -workspace ${WORKSPACE_NAME} -scheme ${YO_SCHEME} -sdk iphoneos -configuration
"${CONFIGURATION}"
clean build
# build project
#xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphoneos BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" CONFIGURATION_BUILD_DIR="${IPHONE_DEVICE_BUILD_DIR}/arm64" SYMROOT="${SYMROOT}" ARCHS='arm64' VALID_ARCHS='arm64' $ACTION
#xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphoneos BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" CONFIGURATION_BUILD_DIR="${IPHONE_DEVICE_BUILD_DIR}/armv7" SYMROOT="${SYMROOT}" ARCHS='armv7 armv7s' VALID_ARCHS='armv7 armv7s' $ACTION
# Copy the framework structure to the universal folder (clean it first)
#
因爲
framework
的合併
,lipo
僅僅是合併了最後的
二進制可運行文件
,
因此其餘的需要咱們本身複製過來
#
先移除原來的
rm -rf
"${UNIVERSAL_OUTPUTFOLDER}"
mkdir -p
"${UNIVERSAL_OUTPUTFOLDER}"
cp -R
"${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework"
"${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework"
#
合併模擬器和真機的架構
lipo -create
"${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}"
"${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"
-output
"${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}"
open
"${UNIVERSAL_OUTPUTFOLDER}"
對於以上的腳本,當中用到了很是多的 xcode提早定義的變量,這些變量怎麼來的呢?你可以在命令行下, 輸入
xcodebuild -workspace YohunlUtilsLib.xcworkspace -scheme YohunlUtilsLib -sdk iphonesimulator -configuration Debug -showBuildSettings > xcodebuild_showBuildSettings.txt
來將所有的xcode提早定義變量都導入到文本
xcodebuild_showBuildSettings.txt 中,而後你就可以選用你需要的啦
固然了,咱們通常都應該合併release模式的,而不是debug模式的framework,這個可以在
ccmmand+B 編譯
假設不出錯的話,應該會彈出
到此處,咱們的framework創建好了,如下可以創建一個測試project來測試一下,可否夠了
4)創建一個single View Application的測試上述framework的project TestFrameworkDemo
將上一步生成的 Release-universal/YohunlUtilsLib.framework 增長到project TestFrameworkDemo
再寫一個測試用例:
執行,這時候,你會收到錯誤提示
還記得前面說過吧,咱們打包的framework並不包括
此時,咱們有兩種方式解決問題:
1. 將咱們生成 YohunlUtilsLib.framework 過程當中生成的libAFNetworking.a 拷貝過來,加入到project中(固然了,你要本身合併 libAFNetworking的真機和模擬器的.a文件) [這樣的方式的優勢是可以保證咱們的YohunlUtilsLib.framework使用到的libAFNetworking和咱們同樣的版本號,保證了兼容性,,但同一時候也添加了集成的複雜性,咱們要申明咱們的framework用到了哪些第三方和第三方的版本號信息]
2.在TestFrameworkDemo project中,添加 podFile文件,在當中添加 依賴 pod 'AFNetworking'
又一次pod install一下 [推薦這樣的方式,因爲,咱們用到的第三方都可以使用pod管理,方便],如下就以這樣的方法來講明
執行,OK!!!
二:使用 pod 的命令 pod lib create 來建立 使用pod的framework
命令例如如下 pod lib create YohunlUtilsPod
注意:各個版本號的cocoapods生成的演示樣例project結構什麼的,會有點不同,比方最新的帶有demo的生成的libproject就自己就是target的名字,但是以前版本號target的名字是 名字_example
pod lib create project名 使會讓你確認4個問題,由此來創建project,4個問題都是很是easy的,你可以自行選擇
注意到了沒?這裏咱們的 YohunlUtilsPod 文件夾下,並無podfile文件,那麼咱們創建的庫要怎麼依賴第三方的啊.這裏就是
YohunlUtilsPod.podspec 文件出場了,在這個文件裏,可以制定咱們生成的framework依賴於什麼.有關於spec文件,是重要文件,原則上,僅僅要咱們把這個文件交給別人,別人就可以加入咱們的lib到project裏了
編輯
YohunlUtilsPod.podspec 文件(此文件其實是 ruby格式的,假設你用ruby相關的編輯器打開,就能高亮顯示了),改動內容爲
end
驗證podspec文件是否正確是使用命令 在podspec所在文件夾下運行
pod lib lint
假設提示錯誤的話,它會給出具體的提示,你照着改動就行了,備註:我已經將源代碼上傳到了github上(你也可以上傳你的私有gitserver,通常公司都應該有本身的gitserver)
更改完後,在project中再增長
YONetwork.h和.
YONetwork.m
再執行 pod install
假設提示
SZ-lingdaiping:Example yohunl$ pod install
Updating local specs repositories
Analyzing dependencies
Fetching podspec for `YohunlUtilsPod` from `../`
[!] Unable to satisfy the following requirements:
- `YohunlUtilsPod (from `../`)` required by `Podfile`
- `YohunlUtilsPod (from `../`)` required by `Podfile`
- `YohunlUtilsPod (= 0.1.0)` required by `Podfile.lock`
那麼刪掉 Podfile.lock文件,因爲咱們更改了podspec文件中的庫的版本了
看到沒,是在 Development Pods目錄下,因爲並無上傳到官方去,因此,在此處是 私有的,因此在Development Pods目錄下 .
接下來,如同第一個同樣,加入一個新的target,加入 run script腳本 ,執行它,獲得
創建一個測試project
注意上面圖裏面的文字,此處因爲咱們的frameworkproject採用dynamic 模式的(上文有論述!!!) 因此需要在Embedded Binaries下加入對應的庫.至於爲何還要加入 AFNetworking.framework,上面也有論述,是因爲咱們打包的.framework並無包括AFNetworking.framework,假設不加上,執行起來,仍是要報錯的哦
好了,第一部分講完了
那麼咱們怎麼使用 像咱們使用其餘開源庫那樣的,僅僅要在demoproject里加入一句
pod ‘
YohunlUtilsPod' 而後pod install一下就可以加入呢?(固然了,這樣的方式,咱們一般是直接源代碼模式的framework加入demoproject)
這就是第二部分要講的.
參考文檔: