你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> ios靜態庫的使用

ios靜態庫的使用

編輯:IOS開發綜合
ios靜態庫的使用 2014-03-11 22:24 3906人閱讀 評論(0) 收藏 舉報

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、如果你沒有完全按步驟來弄,可能會出現如下錯誤,一般用模擬器做的項目可能會遇到這個問題:

 

  1. Undefined symbols for architecture i386:
  2. "_OBJC_CLASS_$_RequestServer", referenced from:
  3. objc-class-ref in ListViewController.o
  4. objc-class-ref in SettingsViewController.o
  5. ld: symbol(s) not found for architecture i386
  6. clang: error: linker command failed with exit code 1 (use -v to see invocation) 不要擔心,去DemoTwo的lib中,把對外接口的.m文件添加進去:

     

    \

     

    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 Active Architecture Only的值設置為Yes

    方法二:把上圖Valid Architectures中的armv7s刪除
    方法三:如果有libxxx.a的源代碼再編譯一個libxxx.a的armv7s版本,然後合並到之前的libxxx.a中

     

    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 文件。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved