iOS製作framework以及引入到工程
Framework常識
蘋果平臺的連結庫除了靜態庫 .a 和 動態庫.dylib 之外,Mac OS/iOS 平臺還可以使用 Framework。Framework 實際上是一種打包方式,將庫的二進位制檔案,標頭檔案和有關的資原始檔打包到一起,方便管理和分發,這樣 Xcode 能夠很容易的將其整合到你的工程中去。
在 iOS 8 之前,iOS 平臺不支援使用動態 Framework,開發者可以使用的 Framework 只有蘋果自家的 UIKit.Framework,Foundation.Framework 等。這種限制可能是出於安全的考慮。換一個角度講,因為 iOS 應用都是執行在沙盒當中,不同的程式之間不能共享程式碼,同時動態下載程式碼又是被蘋果明令禁止的,沒辦法發揮出動態庫的優勢,實際上動態庫也就沒有存在的必要了。
由於上面提到的限制,開發者想要在 iOS 平臺共享程式碼,唯一的選擇就是打包成靜態庫 .a 檔案,同時附上標頭檔案(例如微信的SDK)。但是這樣的打包方式不夠方便,使用時也比較麻煩,大家還是希望共享程式碼都能能像 Framework 一樣,直接扔到工程裡就可以用。於是人們想出了各種奇技淫巧去讓 Xcode Build 出 iOS 可以使用的 Framework,Framework 還有 “偽”(Fake) Framework 和 “真“(Real) Framework 的區別。
iOS 8/Xcode 6 推出之後,iOS 平臺添加了動態庫的支援。為什麼 iOS 8 要新增動態庫的支援?唯一的理由大概就是 Extension 的出現。Extension 和 App 是兩個分開的可執行檔案,同時需要共享程式碼,這種情況下動態庫的支援就是必不可少的了。但是這種動態 Framework 和系統的 UIKit.Framework 還是有很大區別。系統的 Framework 不需要拷貝到目標程式中,我們自己做出來的 Framework 哪怕是動態的,最後也還是要拷貝到 App 中(App 和 Extension 的 Bundle 是共享的),因此蘋果又把這種 Framework 稱為 Embedded Framework。
系統會在需要的時候將framework載入記憶體中,多個應用程式可以同時使用同一個framework。這種方法可以使得你的程式碼易分享,在多個工程中複用,並且可以隱藏實現細節,控制公開的標頭檔案。
蘋果平臺的靜態庫和動態庫的概念與其他平臺的概念不盡相同。
- 靜態庫:連結時完整地拷貝至可執行檔案中,被多次使用就有多份冗餘拷貝。
- 動態庫:連結時不復制,程式執行時由系統動態載入到記憶體,供程式呼叫,系統只加載一次,多個程式共用,節省記憶體。
總結來說:其實就是載入時機和載入次數的不同。
framework分為真機—Debug(除錯)版本、真機—Release(釋出)版本、模擬器—Debug版本、模擬器—Release版本;開發中一般都打包Release(釋出)版本,將真機和模擬器版本合併,提供外界。(注意:debug和release要和引入framework的工程對應
在專案開發的過程中,例如兩個公司之間業務交流,不可能把原始碼都發送給另一個公司,這時候將私密內容打包成framework,別人只能呼叫介面,而不能知道其中實現的細節。
製作framework
步驟
1,新建工程
選擇左邊那個framework,右邊那個是建立.a靜態庫的。
2,建立介面程式碼
在工程中建立類,寫一些開放給外部用的介面。
第一個類,做點簡單的加法,需要公開的api就寫在h裡面
//
// Compute.h
// FrameworkTest
//
// Created by yxhe on 16/7/6.
// Copyright © 2016年 yxhe. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Compute : NSObject
- (NSInteger)addNumber:(NSInteger)a withNumber:(NSInteger)b;
@end
//
// Compute.m
// FrameworkTest
//
// Created by yxhe on 16/7/6.
// Copyright © 2016年 yxhe. All rights reserved.
//
#import "Compute.h"
@implementation Compute
- (NSInteger)addNumber:(NSInteger)a withNumber:(NSInteger)b
{
return a + b;
}
@end
第二個類,列印
//
// Print.h
// FrameworkTest
//
// Created by yxhe on 16/7/6.
// Copyright © 2016年 yxhe. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Print : NSObject
- (void)printWords:(NSString *)words;
@end
//
// Print.m
// FrameworkTest
//
// Created by yxhe on 16/7/6.
// Copyright © 2016年 yxhe. All rights reserved.
//
#import "Print.h"
@implementation Print
- (void)printWords:(NSString *)words
{
NSLog(@"%@", words);
}
@end
最後在自動生成的匯出標頭檔案裡把這兩個類的h都import進去
//
// FrameworkTest.h
// FrameworkTest
//
// Created by yxhe on 16/7/6.
// Copyright © 2016年 yxhe. All rights reserved.
//
#import <UIKit/UIKit.h>
//! Project version number for FrameworkTest.
FOUNDATION_EXPORT double FrameworkTestVersionNumber;
//! Project version string for FrameworkTest.
FOUNDATION_EXPORT const unsigned char FrameworkTestVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <FrameworkTest/PublicHeader.h>
#import <FrameworkTest/Compute.h>
#import <FrameworkTest/Print.h>
3,修改buildsettings
選擇TARGETS裡面
- Linking下面:Dead Code Stripping選No,Mach-O Type選擇static或者dynamic都行(在後面有點區分)
- Architectures下增加armv7s,並選中。將Build Active Architecture Only 設定為NO
4,設定Headers
把需要公開呼叫的標頭檔案一道public裡面,需要隱藏的放在private裡面。
5,打包framework
- 選擇一個模擬器,編譯工程(command + B)
- 選擇一個真機或者Generic Device(建議),編譯工程(command + B)
- 在Finder中找到.framework檔案
生成成功後工程目錄裡的product從紅色變成了正常顏色。
會看到分成了 模擬器和真機兩個資料夾,此時就可以把這兩個.framework分別引入到對應的模擬器或者真機專案使用了。
6,合併framework
通過終端命令將兩個framework合為一個模擬器和真機都可使用的framework。
開啟控制檯輸入
lipo -create iphoneos下frameworkTest的路徑 simulator下frameworkTest的路徑 -output 新的路徑,這樣就完成了模擬器和真機版本的合併,新路徑下的frameworkTest就是你合併後的檔案,將這個檔名字改成和你未合併之前的Test一樣的名字,放到framework資料夾下,替換掉原來的frameworkTest檔案。
例如我的
lipo -create /Users/yxhe/Library/Developer/Xcode/DerivedData/FrameworkTest-gajdcpnghsabghalspugjmolohhd/Build/Products/Debug-iphoneos/FrameworkTest.framework/FrameworkTest /Users/yxhe/Library/Developer/Xcode/DerivedData/FrameworkTest-gajdcpnghsabghalspugjmolohhd/Build/Products/Debug-iphonesimulator/FrameworkTest.framework/FrameworkTest -output /Users/yxhe/Library/Developer/Xcode/DerivedData/FrameworkTest-gajdcpnghsabghalspugjmolohhd/Build/Products/FrameworkTest
在目標資料夾裡找到那個FrameworkTest檔案,拖到真機.framework資料夾裡替換掉原來的,這樣,真機.framework就可以當做universal的framework使用了。
tips
- 合併還有另外一種方法,沒試成功,是建立Aggregate跑指令碼,不會寫T_T
- 帶有圖片資源的需要把圖片打包成Bundle檔案,和framework一起拷貝到相應的專案中。
- 公開的類中如果引用的private的類,打包以後對外會報錯,找不到那個private的類,可以把那個private的.h放到private裡面
使用framework
引用系統framework
1)選中專案名稱
2)選中TARGETS
3)選中Build Phases
4)在Link Binary With Libraries中新增。
5)點選“+”號,加入所需的系統提供的.framework檔案
6)引入標頭檔案,就行了
引用第三方framework
步驟
這裡主要使用之前建立的framework測試
1)從Finder裡面直接將.framework檔案直接拖到工程目錄中
2)在Link Binary With Libraries中新增framework
其實一般拖進去之後,這個項系統自動添加了
注意:如果是Dynamic Libray形式的framework,需要新增到Embedded Frameworks
3)在專案中引入標頭檔案即可
測試結果
目錄結構
程式碼的寫法
//
// ViewController.m
// FrameWorkUseTest
//
// Created by yxhe on 16/7/6.
// Copyright © 2016年 yxhe. All rights reserved.
//
#import "ViewController.h"
// 引入framework的標頭檔案
#import <FrameworkTest/Compute.h>
#import <FrameworkTest/Print.h>
@interface ViewController ()
@end
@implementation ViewController
- (IBAction)onClick:(id)sender
{
Compute *compute = [[Compute alloc] init];
NSInteger sum = [compute addNumber:7 withNumber:5];
Print *print = [[Print alloc] init];
[print printWords:[NSString stringWithFormat:@"7 + 5 = %ld", (long)sum]];
_numberLabel.text = [NSString stringWithFormat:@"7 + 5 = %ld", (long)sum];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
在模擬器和iphone6s真機上都成測試成功