ios的靜態庫文件是*.a,如果需要使用它,我今天學的簡單的方法,可通過,簡單說說,如果有一個A手機項目,一個B的靜態庫項目,A想使用B.a,按以下的步驟:
1、在A項目裡面拖進B項目。在B的product下面會看見紅色的B.a,表示還沒有編譯通過(在工程設置裡添加上你需要導出的.m文件)。
2、選擇好需要編譯的對象,B下的某模擬器或者是B下的真機上進行編譯(模擬器上生成的靜態庫和真機上生成的不能混用)
3、在A裡面新建一個文件夾(new group),裡面拖進你需要B裡面導入的頭文件。
4、在A的framework裡加入編譯好的.a靜態庫,編譯通過的就不會是紅色。
5、在需要使用的地方#import 所需的頭文件。ok了!
下面二篇,是別人寫的生成和使用靜態庫,裡面還有資源的綁定方法,可借鑒!
一、IOS開發----生成靜態庫(.a)
由於iPhone控件的極度匮乏和自定義組件在重用上的限制,在過去的項目中我們積累了大量的“純代碼”組件——因為IB本身的限制,我們無法把這些組件封裝為IB組件庫(本來我們想通過分發xib文件的方式重用這些組件,但最終發現這根本不可能,蘋果的Plug-in編程不支持iPhone)。
最終我們想到了靜態庫。雖然這仍然還是一種比較原始的復用方式,但起碼我們可以隱藏組件的源代碼。
下面, 我們使用iPhone靜態庫把自定義組件CheckButton 進行進一步的封裝。(組件的實現參考前一篇博文《自定義控件復選框和單選框的實現》)
一、實現靜態庫
新建工程, 選擇 Library 下的 “ Cocoa Touch Static Library ” 。給工程命名,例如:yhyLibrary。
復制CheckButton組件的4個源文件:CheckButton.h、CheckButton.m、RadioGroup.h、RadioGroup.m到Classes目錄下,同時把CheckButton的4個資源文件:check.png、uncheck.png、radio.png、unradio.png,復制到工程文件夾。
按下 ? +b編譯,在Products目錄下即產生一個 .a文件。
二、 新建資源束
靜態庫中並不能包含資源文件,雖然我們已經把4個資源文件(.png文件)拷貝到靜態庫工程中,但實際上這些.png是不會添加到target的,也就是說編譯結果中並不包含這些資源,因此如果此時調用靜態庫,所有的資源(字符串、圖片)都是缺失的。
我們可以把資源建立成單獨的束(Bundle)。
新建工程“ Mac OS X -> Framework & Library -> Bundle ”,命名為:yhyLibraryBundle。
然後把上面4個.png文件拷進Resouces中去。編譯,生成yhyLibraryBundle.bundle文件。
返回靜態庫工程,新建一個類:Utils 。
編輯Utils.h:
#define MYBUNDLE_NAME @ "yhyLibraryBundle.bundle"
#define MYBUNDLE_PATH [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: MYBUNDLE_NAME]
#define MYBUNDLE [NSBundle bundleWithPath: MYBUNDLE_PATH]
NSString * getMyBundlePath( NSString * filename);
編輯Utils.m:
#import "Utils.h"
NSString* getMyBundlePath( NSString * filename)
{
NSBundle * libBundle = MYBUNDLE ;
if ( libBundle && filename ){
NSString * s=[[libBundle resourcePath ] stringByAppendingPathComponent : filename];
NSLog ( @"%@" ,s);
return s;
}
return nil ;
}
函數getMyBundlePath可以取得束yhyLibraryBundle中具體資源的絕對文件路徑,如:
/Users/kmyhy/Library/Application Support/iPhone Simulator/4.2/Applications/8213652F-A47E-456A-A7BB-4CD40892B66D/yhyLibTest.app/yhyLibraryBundle.bundle/Contents/Resources/radio.png
同時,修改CheckButton.m中的代碼,導入Utils.h頭文件,把其中獲取圖片的代碼由imageNamed修改為imageWithContentsOfFile,如:
[ icon setImage :[ UIImage imageWithContentsOfFile : getMyBundlePath ( checkname )]];
即通過絕對路徑讀取圖片資源。
除了這種方法,我們還可以有一個簡單辦法,就是把4個資源文件直接拷貝到你調用靜態庫的應用工程中(不需要修改靜態庫代碼)。
三、靜態庫調用
1、添加靜態庫
新建Window-based Application工程,給工程命名,如yhyLibraryTest。
右鍵點 Frameworks->Add->Existing Files.. ,把靜態庫工程的yhyLibrary.xcodeproj文件 添加到當前工程(不要選擇 Copy items ) 。
選中添加進來的yhyLibrary.xcodeproj文件,勾選“include to target”選項,如下圖,打上最後一個小勾:
2、添加Direct Dependencies(即引用工程)
類似於Visual Studio中的引用工程,目的是便於在本工程中直接編輯所引用的靜態庫工程,以便對靜態庫進行修改。
在“ Targets ”目錄下選擇“ FirstLibraryTest ”,點擊“info”按鈕,調出目標的屬性窗口,切換到“General”欄,點擊“ Direct Dependencies ”下方的“ + ”按鈕,將工程靜態庫libyhyLibrary添加到Direct Dependencies中,結果如下圖:
3、添加頭文件搜索路徑
打開工程的info窗口,在Build欄中找到Header Search Paths,添加字符串“../yhyLibrary”。
4、 引用資源束
在target的Copy Bundle Resources上右鍵,選擇“Add->Existing File…”,把前面生成的yhyLibraryBundle.bundle束添加到工程。
5、調用靜態庫中的類
編輯 application:( UIApplication *)application didFinishLaunchingWithOptions: 方法中的代碼:
// 單選按鈕組
RadioGroup * rg =[[ RadioGroup alloc ] init ];
// 第 1 個單選按鈕
CheckButton * cb=[[ CheckButton alloc ] initWithFrame : CGRectMake ( 20 , 60 , 260 , 32 )];
// 把單選按鈕加入按鈕組
[ rg add :cb];
cb. label . text = @"★" ;
cb. value =[[ NSNumber alloc ] initWithInt : 1 ];
// 把按鈕設置為單選按鈕樣式
cb. style = CheckButtonStyleRadio ;
// 加入視圖
[ self . window addSubview :cb];
[cb release ]; //add 後,會自動持有,可以釋放
// 第 2 個單選按鈕
cb=[[ CheckButton alloc ] initWithFrame : CGRectMake ( 20 , 100 , 260 , 32 )];
[ rg add :cb];
cb. label . text = @"★★" ;
cb. value =[[ NSNumber alloc ] initWithInt : 2 ];
cb. style = CheckButtonStyleRadio ;
[ self . window addSubview :cb];
[cb release ];
// 第 3 個單選按鈕
cb=[[ CheckButton alloc ] initWithFrame : CGRectMake ( 20 , 140 , 260 , 32 )];
[ rg add :cb];
cb. label . text = @"★★★" ;
cb. value =[[ NSNumber alloc ] initWithInt : 3 ];
cb. style = CheckButtonStyleRadio ;
[ self . window addSubview :cb];
[cb release ];
運行結果如下:
6、分發靜態庫
將生成的.a文件和.bundle文件打包分發給其他人。
二、使用靜態鏈接庫(Xcode4.6.2)
一、理論部分
在實際的編程過程中,通常會把一些公用函數制成函數庫,供其它程序使用,一則提搞了代碼的復用;二則提搞了核心技術的保密程度。所以在實際的項目開發中,經常會使用到函數庫,函數庫分為靜態庫和動態庫兩種。和多數人所熟悉的動態語言和靜態語言一樣,這裡的所謂靜態和動態是相對編譯期和運行期的:靜態庫在程序編譯時會被鏈接到目標代碼中,程序運行時將不再需要改靜態庫;而動態庫在程序編譯時並不會被鏈接到目標代碼中,只是在程序運行時才被載入,因為在程序運行期間還需要動態庫的存在。
靜態鏈接庫適用於:
1.你想將一部分以後都不會修改的代碼打包,供其他項目使用
2.你想將一部分代碼封裝起來給別人用,又不願別人看到你的實現方法
二、實踐部分
如何制作靜態鏈接庫(以下簡稱lib):
1。如果是新工程。創建工程的時候選Framework&Library -> cocoa touch static library,就直接創建了一個靜態鏈接庫工程,默認會有兩個跟工程名相同的.h和.m,繼續添加文件,m都會自動加入到Build Phases->Compile Source中,表示這些代碼會被編譯進lib中,你可以刪掉你不希望被編譯的。
2. 如果是項目工程,想抽取一個lib出來,就add target,也是選Framework&Library -> cocoa touch static library。在xcode navigator裡會多一個文件夾,和你新創建的target同名。同樣,你可以在Build Phases->Compile Source裡,添加你希望加入到lib中的文件。
下面:新建兩個單視圖模版項目DemoOne,DemoTwo,其中,我想把DemoTwo作為靜態庫,然後在DemoOne中使用:
a、打開DemoTwo
鼠標選擇:
然後 點擊Add Target,選擇 Framework & Library -> Cocoa Touch Static Library -> 新建一個名字叫MyLib的庫:
其中,MyLib這個target,就是我們想對外提供的庫,這個庫的對外提供的接口,是我們自己可以任意控制的,當然可以加多個target,每個target靜態庫可以提供不同接口,我這裡只做一個靜態庫MyLib。讓MyLib這個target 和 DemoTwo 建立時的默認target DemoTwo功能類似,所以還要給MyLib 添加 Frameworks:
然後開始編寫MyLib這個庫想對外提供哪些功能了,在DemoTwo項目中建立一個group,命名為LibMethod,並在其中新建三個類Func1,Funk2,UILabelEX,其中實現的代碼都很簡單,打印log而已。
Func1 和 Func2 類似,拿Func1舉例:
@interface Func1 : NSObject
- (void) func1Log;
@end
#import "Func1.h"
@implementation Func1
- (void) func1Log {
NSLog(@"Func1 log");
}
@end
UILabelEX 是一個類別擴展,因為之前有人說,類別擴展不能放到靜態庫中,所以親自試驗一下:
#import
@interface UILabel (TestColor)
- (void) testMethodColor;
@end
#import "UILabelEX.h"
@implementation UILabel (TestColor)
- (void) testMethodColor {
NSLog(@"testMethodColor");
}
@end
並且確保MyLib 的 Compile Sources 中包含我們剛剛創建類的 .m文件,因為這裡添加了哪些.m文件,就相當於MyLib靜態庫對外提供了什麼接口,如果沒有加入,就要手動點擊+來加入了:
然後關閉DemoTwo項目,打開DemoOne項目,打開DemoTwo項目文件夾,把其中的 DemoTwo.xcodeproj 拖拽到DemoOne中:
然後給DemoOne添加庫,選擇我們在DemoTwo中創建的MyLib:
如果libMyLib.a為紅色,表明DemoTwo,沒有編譯生成libMyLib.a,不要慌,這個是小事情:
理論(在編譯之前,在target的scheme中選build configuration選擇模擬器,然後編譯。
注意,你用device模式編譯出的lib只能真機運行,模擬器模式編譯出的lib只能用於模擬器調試。然後找到編譯出lib,復制到需要它的工程裡。
如果你希望一個lib既可以在模擬器上運行,又可以在真機上運行,那就各編譯一次吧,把兩個lib都找到,用命令把兩個lib合並成一個,命令是:lipo -create sim.a dev.a -ouput libXX.a 合並產生的libXX.a就可以兩用了。
把lib和新工程裡需要引用的頭文件都添加進新工程,這樣就可以了。)
我這裡以使用模擬器為例,來讓DemoTwo編譯生成lib
這個有個細節問題,就是你生成的lib想用在真機,還是模擬器?很簡單, 首先選擇 Mylib,然後在點擊其響應下的 Edit Scheme,最上邊可以選擇是模擬器還是真機,然後build一下:
此時發現DemoOne中的,依賴庫正常了吧:
然後就開始讓DemoOne來使用DemoTwo提供的借口吧:
單首先要在DemoOne中引入下DemoTwo中的接口,DemoOne新建group ,名字 lib,然後把DemoTwo中的接口.h文件,以引用的形式拖拽到lib文件夾中(如果不以引用形式,當DemoTwo中接口代碼改變時,DemoOne中的接口文件不會隨著改變):
到此時,工作基本完成了,然後在DemoOne的 ViewController中實現如下代碼:
#import "ViewController.h"
#import "Func1.h"
#import "Func2.h"
@implementation ViewController
- (void)viewDidLoad
{
[superviewDidLoad];
Func1 *obj1 = [[Func1alloc]init];
[obj1 func1Log];
Func2 *obj2 = [[Func2alloc]init];
[obj2 func2Log];
}
@end
編譯及運行,不出意外,應該有log打印了,說明我們基本成功了。
不過是不是少了點是嗎?對 #import "UILabelEX.h" 這個類還沒使用呢,這個類是對UILabel的類型擴展:
#import
@interface UILabel (TestColor)
- (void) testMethodColor;
@end
#import "UILabelEX.h"
@implementation UILabel (TestColor)
- (void) testMethodColor {
NSLog(@"testMethodColor");
}
@end
那使用一下吧:
UILabel *obj3 = [[UILabel alloc]init];
[obj3 testMethodColor];
然後編譯並運行,發現項目crash了,原因:-[UILabel testMethodColor]: unrecognized selector sent to instance 0x7574190
代碼裡明明UILabel可以找到testMethodColor,但實際上沒找到這個方法,這個地方還有個細節,答案在這裡:http://developer.apple.com/library/mac/#qa/qa1490/_index.html
DemoOne的 build setting中,搜索 Other Linker Flags,找到設置後,在其中添加一個參數 -ObjC,再編譯及運行,貌似一切都OK了。
其它:
1、如果你沒有完全按步驟來弄,可能會出現如下錯誤,一般用模擬器做的項目可能會遇到這個問題:
2、如果做真機使用的 lib,可能會遇到arm7相關的錯誤,我暫時沒遇到,引用別人的解決辦法如下(我沒親自試驗過):
Xcode4.5.2、iOS6應用中靜態庫不支持armv7s的解決方法
錯誤詳細信息:
ld: file is universal (3 slices) but does not contain a(n) armv7s slice: /zhangyg/XXX/XXX/libs/libxxx.a for architecture armv7s
clang: error: linker command failed with exit code 1 (use -v to see invocation)
解決方法如下:
方法一:把上圖Build
3、模擬器運行正常,但真機會crash,打印如下錯誤:
dyld: lazy symbol binding failed: Symbol not found: _objc_setProperty_atomic_copy
Referenced from: /var/mobile/Applications/DFCB17A5-52AC-41CD-9ECB-94415C7D36F3/kalagame-demo.app/kalagame-demo
Expected in: /usr/lib/libobjc.A.dylib
dyld: Symbol not found: _objc_setProperty_atomic_copy
Referenced from: /var/mobile/Applications/DFCB17A5-52AC-41CD-9ECB-94415C7D36F3/kalagame-demo.app/kalagame-demo
Expected in: /usr/lib/libobjc.A.dylib
解決辦法:
這個錯誤就是說App可執行文件裡引用了objc_setProperty_nonatomic或objc_setProperty_atomic這些函數。但是代碼裡顯然沒有直接調用這2個函數,應該是系統在編譯時生成的。經過Debug調試發現總是在設置一個對象的屬性時出現這個錯誤。而這個對象的類定義在靜態庫裡面,所以我看了看靜態庫。
經過排查,發現導致這一問題的原因是這個靜態庫的Deployment Target設置成了6.0。因為objc_setProperty_nonatomic和objc_setProperty_atomic是iOS6中新增的函數,所以如果靜態庫的Deployment Target設置成iOS6,那麼編譯後就會使用objc_setProperty_nonatomic和objc_setProperty_atomic這些新的API。由於iOS5中沒有這些API,運行後將會崩潰。
結論
靜態庫在編譯時,Deployment Target一定要低於和等於工程的Deployment Target。否則容易出現低版本iOS運行不兼容的情況。
4、突然項目要更改靜態庫項目的工程名稱,於是右件靜態庫項目可執行文件(藍顏色的那個),彈出菜單中選 Show File Inspector,然後XIB屬性修改入口彈出,可以修改項目名稱,但修改完項目名稱之後,發現靜態庫提供的幾個接口失效了。
報錯:Undefined symbols for architecture armv7:
解決辦法:在引用靜態庫的項目設置中,重新添加靜態庫文件,就是那個 XXX.a 文件。