做者:shede333
主頁:http://my.oschina.net/shede333
版權聲明:原創文章,版權聲明:自由轉載-非商用-非衍生-保持署名 | [Creative Commons BY-NC-ND 3.0][]html
本人英語也不是太好,翻譯質量不是過高,若有不妥之處,歡迎指點批評。ios
點此查看文章 英文原文shell
#建立IOS靜態庫xcode
若是你開發ios有一段時間了,你可能有許多想在你大部分項目裏重用的類和工具函數。網絡
重用代碼最容易的方法就是複製/黏貼,可是,這在代碼維護上很快就變成一個噩夢。 既然每個app都擁有一份 共享代碼
的拷貝,這就很難保證全部 拷貝的代碼
與 共享代碼
在bug修正與更新的同步(一致性)。架構
這裏就使用靜態庫來拯救噩夢。靜態庫就是類、函數、定義(definitions)和資源的一個包,使用靜態庫,你能把代碼打包在一塊兒,而且在你全部的項目間共享。app
在這個教程,你將親身經歷使用兩種不一樣的方法建立你本身的通用靜態庫。框架
你應該熟悉Objective-C and iOS開發,才能理解大體上這個教程。
若是你對怎樣作一個相同的app 以及 圖像濾光代碼在庫裏的工做原理 感興趣,Core Image
的相關知識雖然不是必須的,可是對你會頗有幫助。dom
準備開始高效的減小、重用和循環使用你的代碼吧!iphone
##爲何使用靜態庫
你可能由於不少緣由而建立靜態庫:
在這個教程裏,假設你已經看過**Core Image Tutorial**這個教程,而且理解了如何使用一些照片處理效果的代碼。
你將會把那些代碼添加到靜態庫裏,而且在一個修改過的app裏使用靜態庫。最終將會獲得一個相同的app,可是會體現出面陳提到的全部優勢。
打開Xcode,選擇File\New\Project,當Choose a template對話框出現時,選擇iOS\Framework & Library\Cocoa Touch Static Library,以下圖:
點擊Next,在項目選項對話框,輸入ImageFliters做爲項目名稱(project name),而後輸入一個惟一的公司標識(company identifier ),而且勾選Use Automatic Reference Counting,而Include Unit Tests不要勾選。
點擊Next,最後選擇一個你想要保存項目的位置,而後點擊Create。
Xcode已經建立了一個準備使用靜態庫的項目,而且項目裏已經自動添加了ImageFliters類。(這難道不是Xcode的優勢麼?)這就是你將要編寫的圖像濾光代碼的地方。
注意:你能夠你想要的任何類到靜態庫裏,甚至刪除原來的代碼(ImageFliters類)。 在這個教程,全部代碼將會添加到Xcode開始自帶的類ImageFliters。
既然你項目仍然空,讓咱們添加一些代碼進去吧!
##項目:Image Filters
這個庫是爲ios設計的,而且使用了UIKit框架,因此,你要作的第一件事是:在頭文件,導入(import)UIKit框架,打開ImageFilters.h
,而且在該文件最頂部添加下列代碼:
#import <UIKit/UIKit.h>
接下來,在文件@interface ImageFilters : NSObject
這一行的下面,黏貼以下聲明代碼:
@property (nonatomic,readonly) UIImage *originalImage; - (id)initWithImage:(UIImage *)image; - (UIImage *)grayScaleImage; - (UIImage *)oldImageWithIntensity:(CGFloat)level;
這些頭文件的聲明代碼,定義了類的公開接口。當其餘開發者(包括你本身)使用這個庫,經過讀這個頭文件,他們就知道類名和內嵌的庫中的方法。
如今,是時候添加實現代碼
(implementation)了, 打開ImageFilters.m
,在這行 #import "ImageFilters.h"
下面黏貼以下代碼:
@interface ImageFilters()
@property (nonatomic,strong) CIContext *context; @property (nonatomic,strong) CIImage *beginImage;
@end
上面的代碼聲明瞭許多用於類內部的屬性,這些屬性並非公開的,因此任何使用這個庫的app都不能訪問這些屬性。
最後,你須要實現類中的方法,在這行@implementation ImageFilters
下面黏貼以下代碼:
- (id)initWithImage:(UIImage *)image { self = [super init]; if (self) { _originalImage = image; _context = [CIContext contextWithOptions:nil]; _beginImage = [[CIImage alloc] initWithImage:_ originalImage]; } return self; } - (UIImage*)imageWithCIImage:(CIImage *)ciImage { CGImageRef cgiImage = [self.context createCGImage:ciImage fromRect:ciImage.extent]; UIImage *image = [UIImage imageWithCGImage:cgiImage]; CGImageRelease(cgiImage); return image; } - (UIImage *)grayScaleImage { if( !self.originalImage) return nil; CIImage *grayScaleFilter = [CIFilter filterWithName:@"CIColorControls" keysAndValues:kCIInputImageKey, self.beginImage, @"inputBrightness", [NSNumber numberWithFloat:0.0], @"inputContrast", [NSNumber numberWithFloat:1.1], @"inputSaturation", [NSNumber numberWithFloat:0.0], nil] outputImage; CIImage *output = [CIFilter filterWithName:@"CIExposureAdjust" keysAndValues:kCIInputImageKey, grayScaleFilter, @"inputEV", NSNumber numberWithFloat:0.7], nil].outputImage; UIImage *filteredImage = [self imageWithCIImage:output]; return filteredImage; } - (UIImage *)oldImageWithIntensity:(CGFloat)intensity { if( !self.originalImage ) return nil; CIFilter *sepia = [CIFilter filterWithName:@"CISepiaTone"]; [sepia setValue:self.beginImage forKey:kCIInputImageKey]; [sepia setValue:@(intensity) forKey:@"inputIntensity"]; CIFilter *random = [CIFilter filterWithName:@"CIRandomGenerator" ; CIFilter *lighten = [CIFilter filterWithName:@"CIColorControls"]; [lighten setValue:random.outputImage forKey:kCIInputImageKey]; [lighten setValue:@(1 - intensity) forKey:@"inputBrightness"]; [lighten setValue:@0.0 forKey:@"inputSaturation"]; CIImage *croppedImage = [lighten.outputImage imageByCroppingToRect:[self.beginImage extent]]; CIFilter *composite = [CIFilter filterWithName:@"CIHardLightBlendMode"]; [composite setValue:sepia.outputImage forKey:kCIInputImageKey]; [composite setValue:croppedImage forKey:kCIInputBackgroundImageKey]; CIFilter *vignette = [CIFilter filterWithName:@"CIVignette"]; [vignette setValue:composite.outputImage forKey:kCIInputImageKey ; [vignette setValue:@(intensity * 2) forKey:@"inputIntensity"]; [vignette setValue:@(intensity * 30) forKey:@"inputRadius"]; UIImage *filteredImage = [self imageWithCIImage:vignette outputImage]; return filteredImage; }
這上面代碼實現了初始化和執行Core Image
濾光效果的方法。 由於解釋上面Core Image
代碼的功能,超出本教程的範圍, 因此,您能在這個**Core Image Tutorial**教程裏,學習Core Image
和濾光的相關知識。
此時,你的靜態庫擁有ImageFilters
類,這個類公開以下3個方法:
如今編譯而且運行你的庫(將Run按鈕旁邊的編譯目標改成iOS Device,這樣便於後面找到.a文件), 你將會注意到,點擊Xcode的**"Run"**按鈕,只執行編譯; 實際上,你不能經過運行你的靜態庫來看到結果,由於尚未app支持它。
靜態庫使用.a
做爲擴展名,而不是.app
、.ipa
等。 生成的靜態庫在Xcode導航欄的Products文件夾裏, 右擊 或者 「按住CTRL,單擊」 libImageFilters.a
,在彈出的菜單裏選擇Show in Finder,以下圖:
Xcode將會在iFinder打開文件夾,你會看到以下:
一個共享庫的最終最終產品有2個部分:
ImageFilters.h
文件。 你在以後的app項目裏面,爲了讓Xcode在編譯時知道 輸出類(the exported classes),須要用到這個頭文件。ImageFilters.a
。當你想要在裏使用這個庫的時候,你須要使用這個文件來鏈接靜態庫。導入新框架(framework)到app,與導入靜態庫很類似。 你只須要簡單的導入框架頭文件(framework header)和鏈接到app的框架代碼(framework code)。
靜態庫使用小警告:默認狀況下,編譯生成的庫文件僅僅適用於Xcode當前指定的架構(真機的框架爲ARM,模擬器框架爲i386)
。
假如你目前的編譯目標爲模擬器,庫文件將包含適用於i386架構的對象代碼;
假如你目前的編譯目標爲真機設備,庫文件將包含適用於ARM架構的對象代碼;
你須要編譯出兩個版本的靜態庫文件,當你的編譯目標在真機和模擬器間切換時,要鏈接使用對應的庫文件。
那要怎麼作才能解決上面的問題呢?
幸運的是,有一個很好的辦法, app項目不須要建立多個配置或者最終產品(build products),就能支持多個平臺。 您能夠建立一個universal binary,它包含這兩個架構的對象代碼。
##Universal Binaries(通用二進制)
通用二進制 是一種包含多個架構對象代碼的特殊二進制文件。 在Mac電腦的cpu從PowerPC (PPC) 過渡到 Intel (i386) 時,你對 通用二進制 可能會熟悉。 在過渡期間,Mac上的app一般裝載了通用二進制,通用二進制在一個二進制文件內包含了兩個平臺的可執行代碼,以便app在 Intel 和 PowerPC 架構的Mac電腦上都可運行。
支持ARM和i386架構的概念並無太多區別。既然這樣,靜態庫文件將包含適用於ios設備(ARM)和模擬器(i386)代碼。Xcode將會識別通用二進制,而且你沒戲編譯app時,Xcode會根據當前的編譯目標,選擇適當的架構。
爲了建立通用二進制庫,你須要使用一個系統工具**lipo**。
不用擔憂小貓咪,lipo並非指的上圖中的脂肪(這句話屬於美式幽默,我也不太懂)。
lipo 是一種容許你在通用二進制文件進行操做(操做包括:建立通用二進制文件、顯示文件內容等等)的命令行工具。 在這個教程裏,你將會使用lipo,將不一樣架構的二進制文件 合併成 包含多個架構的內容的單獨二進制文件。 你能在命令行裏直接使用lipo,可是在這個教程裏面,你將會經過運行 一個命令行腳原本讓Xcode爲你工做,這個命令行能建立通用二進制文件。
在Xcode,一個聚合目標(An Aggregate Target)將會一次編譯多個目標(target),包括命令行腳本。 在Xcode菜單欄裏操做File/New/Target,出現以下對話框,選擇** iOS/Other**,再點擊Aggregate,以下圖
在 Product Name
輸入UniversalLib,確保Project
欄選中ImageFilters項目,以下圖
在項目導航欄上點擊ImageFilters(以下圖操做1),而後選擇UniversalLib目標(以下圖操做2),切換到到Build Phases標籤(以下圖操做3), 這裏就是你將建立行爲的地方,當目標被編譯時,這裏的行爲將會運行。
點擊右下角Add Build Phase按鈕,在彈出菜單裏選擇Add Run Script,以下圖:
如今你要建立一個腳本。展開Run Script模塊,將下面的shell代碼黏貼進去。
# define output folder environment variable UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal # Step 1. Build Device and Simulator versions xcodebuild -target ImageFilters ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ ROOT="${BUILD_ROOT}" xcodebuild -target ImageFilters -configuration ${CONFIGURATION} -sdk iphonesimulator -arch i386 BUILD_DIR="${BUILD_DIR}" BUILD_ ROOT="${BUILD_ROOT}" # make sure the output directory exists mkdir -p "${UNIVERSAL_OUTPUTFOLDER}" # Step 2. Create universal binary file using lipo lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/lib${PROJECT_NAME}.a" # Last touch. copy the header files. Just for convenience cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/include" "${UNIVERSAL_ OUTPUTFOLDER}/"
上面的代碼並不複雜,下面將逐行解釋代碼如何運行:
你的「Run Script 」窗口應該以下圖同樣:
如今你要準備去編譯通用版本的靜態庫。在策略選擇下拉表,選擇聚合目標UniversalLib,以下圖(你的Xcode有一點可能和下圖不一樣,下圖中的「IOS Device」,在你的Xcode可能顯示爲你真實的設備名稱)。
點擊Run按鈕,編譯聚合策略(aggregate scheme)裏選擇的目標。
爲了看到結果,對項目導航欄裏的Product文件夾下的libImageFilters.a右擊,選擇Show in Finder*, 爲了能看到文件夾的上下層次,切換Finder裏顯示文件夾的樣式爲列表樣式,你將會看到一個新文件夾Debug-Universal(若是你在編譯Release版本,文件夾名可能會是Release-Universal),這個文件夾包含通用版本(Universal)靜態庫,以下圖:
你會發現,除了模擬器和設備文件夾,通用靜態庫文件和頭文件都出現了。(這行翻譯可能有誤,原文以下)。
You’ll find the usual headers and static library files are present, except this one links with both the simulator and the device.
以上,就是你爲了建立本身的靜態庫而須要學習的東西。
簡單來講,一個靜態庫項目和一個app項目很類似。你能夠有一個或多個類,最終編譯出來的產品是頭文件個一個裝有代碼的.a文件,這個.a文件就是能被鏈接到多個app裏的靜態庫。
##在本身的App裏使用靜態庫
在本身的App裏使用ImageFilters類與直接從源碼使用 差很少:導入頭文件而且開始使用類。 (這行翻譯可能不太準確,原文以下)
Using the ImageFilters class in your own app is not very different from using it directly from source: you import the header file and start using the class!
問題是,Xcode並不知道頭文件或者二進制文件的位置。
把靜態庫放進項目裏,這有兩個方法:
選擇其中一個方法或者其餘方法,依賴於你本身的選擇: 在你的項目中,是否須要靜態庫的源碼和項目文件。
在這個教程裏,這兩個方法都在下面單獨詳細描述了。你能夠嘗試其中的一個方法,可是最好按照下面描述的順序,把兩個方法都瞭解一下。 在兩個方法的開始部分,你將被要求下載一個zip文件,這個文件是教程**Core Image Tutorial裏的app的一個修改版本。(修改:使用了來自於靜態庫的新類ImageFilters**)。
既然這個教程的目標是叫你怎樣使用靜態庫,這個修改版本包含全部app須要的源碼。 這樣,你就能專一於項目使用靜態庫的配置。
##方法1:引用頭文件和庫的二進制文件(.a文件)
對於這部分教程,你須要下載 starter project for this section。 拷貝下載好的zip文件到磁盤的任意一個文件夾下,解壓zip。你將看到以下的文件夾結構:
爲了讓你方便,靜態庫頭文件和.a文件的副本已經被包含在項目中,你就不用再次拷貝了, 可是項目並無配置好來使用靜態庫,這就是你要作的
打開項目,編譯、運行你的app,你將會看到以下編譯錯誤提示:
如我所料,app沒法找到頭文件。 爲了解決這個問題,你須要在項目裏增長頭文件搜索路徑(Header Search Path),來指出頭文件所在文件夾的位置。使用靜態庫的時候,老是首先配置頭文件搜索路徑(Header Search Path)
以下圖所示,按照圖示的1~4的步驟操做,在第3步點擊了Build Settings標籤後,在搜索欄輸入關鍵字**「header search」**,以便快速定位到咱們配置的那行參數。
雙擊Header Search Paths這行的後半空白部分,將會彈出下圖界面,點擊下圖左下角的「+」按鈕,而後輸入一下信息:
$SOURCE_ROOT/include
$SOURCE_ROOT是Xcode的環境參數,指的是項目的根文件夾(the project’s root folder), Xcode將會使用包含該項目的真實文件夾的路徑地址來代替此參數,這樣,即便你把整個項目移動到別的文件夾或者移動到別的電腦上,你也不用再更改路徑參數了,由於Xcode利用此參數來幫你解決了這些麻煩的問題。
點擊上圖彈出框的外圍部分來關閉彈出框,你將會看到,Xcode已經把參數「$SOURCE_ROOT」轉換爲項目所在文件夾的真實路徑地址,以下圖:
再次編譯運行你的app,你會發現還會存在error,以下所示:
看起來狀況不太好,可是Xcode也給出了部分提示信息。若是你仔細看看error信息,上面編譯出現的「編譯error信息」已經消失,取而代之,如今的error信息是鏈接錯誤(linker errors)。 這就說明,Xcode已經找到頭文件而且用它來編譯app,可是程序鏈接階段,Xcode找不到ImageFliter類對象代碼。 爲何呢?
這很簡單,你還沒告訴Xcode在哪裏你呢個找到包含類實現代碼的庫文件(.a文件)。 (看樣子,這也不是什麼太糟糕的問題)
以下圖所示,按照1~5的步驟操做,
下圖爲「添加庫」界面,點擊左下角的Add Other…按鈕,定位到該項目文件夾裏的lib文件夾,找到libImageFilters.a庫文件,添加進去。
當你作完以上步驟,下圖就應該是你XcodeBuild Phases標籤界面的樣子(注意,libImageFilters.a已經被添加進去了。)
最後一步,在Xcode裏添加**-ObjC**編譯標識, 這個標識有時能夠排除部分靜態庫代碼,只把有效的代碼導入項目。(見下段註解)
-ObjC編譯標識: (此段爲譯者注)
例如,你的項目使用了第三方開源類JSONKit,而你的靜態庫也用到了這個JSONKit,那麼你把靜態庫導入到項目後,這就產生衝突,因此你在把靜態庫導入項目時,靜態庫裏的JSONKit.h頭文件就沒必要導入,由於你的項目裏已經有了JSONKit.h,JSONKit.m文件,使用**-ObjC**編譯標識,Xcode就會變得聰明,Xcode知道你的項目裏已經有了JSONKit的實現代碼,即JSONKit.m文件,因此,靜態庫的JSONKit.m文件就不會被導入,這樣,你的項目和靜態庫就共用你項目裏面的JSONKit.m文件。你能夠作一個相關實驗嘗試一下,把項目和靜態庫裏的JSONKit.m文件裏的內容寫的不同,看看,最終app裏到底使用的哪一個文件。
-ObjC編譯標識功能不少,使用這個標識,可讓靜態庫裏的類和類別(categories)文件被合理的加載進來。 你能夠在**Technical Q&A QA1490學習更多-ObjC**編譯標識相關知識。
添加**-ObjC編譯標識的方法以下圖,點擊Build Settings標籤,在右上角的搜索框輸入Other linker** 便可搜索到。
在Other linker Flags(不用展開該行)這行的後半空白部分雙擊,彈出下圖界面,點擊彈出框左下角的「+」按鈕,輸入-ObjC
便可
最後,編譯、運行你的app;你不會再看到任何編譯error,編譯成功,app將會運行起來,以下圖:
你能夠嘗試點擊app上的按鈕和滑動條,看看具體效果。 執行這些圖片變化效果的代碼並不在app,而是在靜態庫。
恭喜你!你剛剛在一個真實的app裏編譯、運行了你的第一個靜態庫。 包含頭文件和靜態庫文件的方法,已經被用在不少知名的第三方庫裏, 例如AdMob, TestFlight或者那些不想提供源代碼的商業庫(百度地圖、微博分享等等)。
##方法2:將靜態庫做爲子項目加載
對於這部分教程,你須要下載裏一個文件, 點此下載
將下載好的zip文件拷貝你但願的位置,解壓zip,你會看到以下圖的文件結構
若是你看了上面的方法1,你會注意到,此次解壓後的項目文件夾的根目錄和方法1的不一樣: 這個項目文件夾根目錄,沒有任何頭文件和.a文件(由於方法2不須要這些東西)。 取而代之,你將會看到,在本教程開始部分建立的ImageFilters靜態庫項目 做爲子項目被添加到這個項目裏。
在你作其它事情以前,你先編譯、運行這個app,你將會被下圖的error問候:
若是你看過本教程的方法1,你可能已經知道該如何修復這個error。 在你剛剛下載解壓的項目裏,你在ViewController類裏使用了ImageFilters類 ,但你仍然沒有告訴Xcode頭文件在哪裏。你運行該項目時,Xcode找不到ImageFilters.h文件,因此編譯失敗。
爲了把ImageFilters靜態庫項目導入進來做爲子項目,有2個方法:
項目文件(通常爲ImageFilters項目導航欄的最上面的那個文件)
到主項目(這裏是CoreImageFun項目)窗口的導航欄中的任何地方便可,由於目前ImageFilters項目已經在窗口中打開,因此在主項目窗口裏顯示的ImageFilters文件只是單個文件,而不是樹狀結構。你要關閉這兩個項目,而後再打開主項目CoreImageFun,你就會發現它已經變爲樹狀結構。以下圖注意:這兩個方法的最終效果是同樣的(以下圖),在執行上面的方法2,儘可能確保子項目(這裏指的是ImageFilters靜態庫項目)並無在Xcode中打開,
不然的話,你須要關閉這兩個項目,再從新打開主項目。由於同一個項目,不能同時在兩個窗口中打開。
如今Xcode知道了靜態庫子項目,你能夠把添加靜態庫到項目依賴(Dependencies),以下圖操做。 這就意味着,Xcode將確保在編譯app前,靜態庫的代碼是最新的(就是說,若是靜態庫項目有代碼變動,主項目會自動從新編譯靜態庫項目)。
Xcode添加項目依賴,以下圖操做1~4,
點擊上圖步驟4的「+」號按鈕會彈出另外一個窗口,彈出的窗口以下圖,在下拉列表裏選擇ImageFilters目標(而不是universalLib),點擊右下角的「add」,完成。
完成上面的添加依賴的步驟後,Build Phases標籤的Target Dependencies部分,將會增長一條,以下圖:
最後一步,配置項目,將靜態庫鏈接到項目。以下圖,展開Link Binary with libraries部分.
點擊上圖的「+」號按鈕,彈出 下圖的界面, 選擇libImageFilters.a項目,點擊add
添加完成後,Link Binary with Libraries部分以下圖所示:
最後一步,添加**-ObjC**編譯標識,添加步驟與上面的方法1的最後一步同樣,以下圖:
在上圖Other linker Flags行的後半空白部分雙擊,彈出以下圖, 點擊彈出框左下角「+」按鈕,添加 -ObjC
。
編譯運行你的app,app將會運行成功,以下圖:
你能夠操做一下剛剛運行成功的app。
與方法1同樣,圖片效果的邏輯代碼,都在靜態庫中。
若是你按照方法1試驗過添加靜態庫的過程(使用頭文件和靜態庫),在處理方式上,和方法2有不少不一樣之處。 在方法2,你沒有爲Xcode配置header search paths參數。
另外一個不一樣之處,你沒有使用過通用靜態庫(Universal library)。
爲何會不一樣?
把靜態庫項目添加爲子項目,Xcode就幾乎爲你解決了全部事情。
添加子項目和依賴以後,Xcode就知道了在哪裏找到頭文件和靜態庫文件, 並且靜態庫的架構會根據你app選擇的參數來編譯,這真是夠方便的。
若是你使用本身的靜態庫,或者你須要訪問源碼和項目文件, 那麼,導入靜態庫到項目較爲方便的方法:方法2(即添加靜態庫項目做爲子項目); 由於你集成子項目做爲項目依賴,你須要操做、擔憂的事情就不多了,歐耶!
#資源文件打包(這部分爲譯者添加)
在靜態庫裏常常會遇到 圖片
、xib
、各類外部文件
等等,這些不能放在靜態庫裏, 一般的作法是:把這些文件打成一個bundle包(擴展名爲.bundle的文件)。
打bundle包,也能夠建一個target,就像合併兩種架構靜態庫的target的作法差很少。 具體作法點此
#接下來去哪裏?
你能夠在**這裏**下載這個教程的全部項目代碼。
對於靜態庫的基本概念、怎樣在本身的app使用靜態庫,但願這個教程會對你有所幫助。
接下來,用上面的知識去編譯你本身的靜態庫! 你確定有須要添加到全部項目裏的通用類,把這些代碼放在你本身的靜態庫裏重用會是個很好的注意。 你還可以根據功能分類,建立多個靜態庫: 一個靜態庫放網絡交互的代碼, 一個放UI相關類的代碼,等等。 這樣你就能把你須要的模塊添加到項目裏。
爲了進一步鞏固、更深一步學習本教程的內容, 我也推薦你看看蘋果官方文檔裏有關靜態庫的內容 Introduction to Using Static Libraries in iOS
但願你能喜歡這個教程,若是你有任何問題和想法,請加入論壇討論。
#####如下爲譯者注
這篇文章使用Markdown語言編寫,使用了以下工具:
本人英語也不是太好,翻譯質量不是過高,若有不妥之處,歡迎在下面留言,指點批評。