iOS 插件化開發(fā)

framework是Cocoa/Cocoa Touch程序中使用的一種資源打包方式,可以將將代碼文件、頭文件导犹、資源文件、說明文檔等集中在一起羡忘,方便開發(fā)者使用谎痢,作為一名Cocoa/Cocoa Touch程序員每天都要跟各種各樣的Framework打交道。Cocoa/Cocoa Touch開發(fā)框架本身提供了大量的Framework卷雕,比如Foundation.framework/UIKit.framework/AppKit.framework等节猿。需要注意的是,這些framework無一例外都是動態(tài)庫漫雕。

但殘忍的是滨嘱,Cocoa Touch上并不允許我們使用自己創(chuàng)建的framework。不過由于framework是一種優(yōu)秀的資源打包方式浸间,擁有無窮智慧的程序員們便想出了以framework的形式打包靜態(tài)庫的招數(shù)太雨,因此我們平時看到的第三方發(fā)布的framework無一例外都是靜態(tài)庫,真正的動態(tài)庫是上不了AppStore的魁蒜。

iOS上動態(tài)庫可以做什么

和靜態(tài)庫在編譯時和app代碼鏈接并打進同一個二進制包中不同囊扳,動態(tài)庫可以在運行時手動加載,這樣就可以做很多事情兜看,比如:

  • 應(yīng)用插件化

目前很多應(yīng)用功能越做越多锥咸,軟件顯得越來越臃腫。因此插件化就成了很多軟件發(fā)展的必經(jīng)之路细移,比如支付寶這種平臺級別的軟件:

[圖片上傳失敗...(image-bf1911-1575203900463)]

首頁上密密麻麻的功能搏予,而且還在增多,照這個趨勢發(fā)展下去弧轧,軟件包的大小將會不可想象缔刹。目前常用的解決方案是使用web頁面球涛,但用戶體驗和Native界面是沒法比的。

設(shè)想校镐,如果每一個功能點都是一個動態(tài)庫专钉,在用戶想使用某個功能的時候讓其從網(wǎng)絡(luò)下載瞪慧,然后手動加載動態(tài)庫,實現(xiàn)功能的的插件化续语,就再也不用擔心功能點的無限增多了襟己,這該是件多么美好的事引谜!

  • 軟件版本實時模塊升級

還在忍受蘋果動輒一周,甚至更長的審核周期嗎擎浴?有了動態(tài)庫媽媽就再也不用擔心你的軟件升級了员咽!

如果軟件中的某個功能點出現(xiàn)了嚴重的bug,或者想在其中新增功能贮预,你的這個功能點又是通過動態(tài)庫實現(xiàn)的贝室,這時候你只需要在適當?shù)臅r候從服務(wù)器上將新版本的動態(tài)庫文件下載到本地,然后在用戶重啟應(yīng)用的時候即可實現(xiàn)新功能的展現(xiàn)仿吞。

  • 共享可執(zhí)行文件

在其它大部分平臺上滑频,動態(tài)庫都可以用于不同應(yīng)用間共享,這就大大節(jié)省了內(nèi)存唤冈。從目前來看峡迷,iOS仍然不允許進程間共享動態(tài)庫,即iOS上的動態(tài)庫只能是私有的你虹,因為我們?nèi)匀徊荒軐討B(tài)庫文件放置在除了自身沙盒以外的其它任何地方绘搞。

不過iOS8上開放了App Extension功能,可以為一個應(yīng)用創(chuàng)建插件傅物,這樣主app和插件之間共享動態(tài)庫還是可行的夯辖。

PS: 上述關(guān)于動態(tài)庫在iOS平臺的使用,在技術(shù)上都是可行的挟伙,但本人并沒有真正嘗試過做出一個上線AppStore的應(yīng)用楼雹,因此并不保證按照上述方式使用動態(tài)庫一定能通過蘋果審核!

創(chuàng)建動態(tài)庫

image.png
image.png

這邊是PiaoJinDylib

創(chuàng)建你測試類PiaoJin

頭文件部分

#import <Foundation/Foundation.h>

@interface PiaoJin : NSObject

- (void)love;

@end

實現(xiàn)部分

#import "PiaoJin.h"
#import <UIKit/UIKit.h>

@implementation PiaoJin

- (void)love{
    NSLog(@"love you more than I can say!");
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"love you more than I can say by 飄金!" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:@"確定", nil];
    [alertView show];
}

@end

在PiaoJinDylib中引入

#import <UIKit/UIKit.h>

//! Project version number for PiaoJinDylib.
FOUNDATION_EXPORT double PiaoJinDylibVersionNumber;

//! Project version string for PiaoJinDylib.
FOUNDATION_EXPORT const unsigned char PiaoJinDylibVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <PiaoJinDylib/PublicHeader.h>
#import "PiaoJin.h"

設(shè)置開放的頭文件

一個庫里面可以后很多的代碼尖阔,但是我們需要設(shè)置能夠提供給外界使用的接口贮缅,可以通過Target—>Build Phases—>Headers來設(shè)置,如下圖所示:

image.png

我們只需將希望開放的頭文件放到Public列表中即可介却,比如我開放了PiaoJinDylib.h和PiaoJin.h兩個頭文件谴供,在生成的framework的Header目錄下就可以看到這兩個頭文件.一切完成,Run以后就能成功生成framework文件了齿坷。

前面只是我們只是創(chuàng)建了一個動態(tài)庫文件桂肌,但是和靜態(tài)庫類似数焊,該動態(tài)庫并同時不支持真機和模擬器,可以通過以下步驟創(chuàng)建通用動態(tài)庫:

創(chuàng)建Aggregate Target(PiaoJinDylib工程下)

image.png
image.png

我給Aggregate的Target的命名是CommonDylib崎场。

設(shè)置Target Dependencies

按以下路徑設(shè)置CommonDylib對應(yīng)的Target Dependencies:

TARGETS-->CommonDylib-->Build Phases-->Target Dependencies 

將真正的動態(tài)庫PiaoJinDylib Target添加到其中佩耳。

添加Run Script

按以下路徑為CommonDylib添加Run Script:

TARGETS-->CommonDylib-->Build Phases-->Run Script 

添加的腳本為:

# Sets the target folders and the final framework product. 
FMK_NAME=${PROJECT_NAME} 
 
# Install dir will be the final output to the framework. 
# The following line create it in the root folder of the current project. 
INSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework 
 
# Working dir will be deleted after the framework creation. 
WRK_DIR=build 
DEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.framework 
SIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework 
 
# -configuration ${CONFIGURATION}  
# Clean and Building both architectures. 
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphoneos clean build 
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator clean build 
 
# Cleaning the oldest. 
if [ -d "${INSTALL_DIR}" ] 
then 
rm -rf "${INSTALL_DIR}" 
fi 
 
mkdir -p "${INSTALL_DIR}" 
 
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/" 
 
# Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product. 
lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/${FMK_NAME}" 
 
rm -r "${WRK_DIR}" 

添加以后的效果:

image.png

腳本的主要功能是:

1.分別編譯生成真機和模擬器使用的framework;

2.使用lipo命令將其合并成一個通用framework谭跨;

3.最后將生成的通用framework放置在工程根目錄下新建的Products目錄下干厚。

如果一切順利,對CommonDylib target執(zhí)行run操作以后就能生成一個如圖所示的通用framework文件了:

image.png
image.png

使用動態(tài)庫

實際過程中動態(tài)庫是需要從服務(wù)器下載并且保存到app的沙盒中的,這邊直接模擬已經(jīng)下載好了動態(tài)庫并且保存到沙盒中:

image.png

使用動態(tài)庫

使用NSBundle加載動態(tài)庫

- (IBAction)loadFrameWorkByBundle:(id)sender {
    //從服務(wù)器去下載并且存入Documents下(只要知道存哪里即可),事先要知道framework名字,然后去加載
    NSString *frameworkPath = [NSString stringWithFormat:@"%@/Documents/PiaoJinDylib.framework",NSHomeDirectory()];
    
    NSError *err = nil;
    NSBundle *bundle = [NSBundle bundleWithPath:frameworkPath];
    NSString *str = @"加載動態(tài)庫失敗!";
    if ([bundle loadAndReturnError:&err]) {
        NSLog(@"bundle load framework success.");
        str = @"加載動態(tài)庫成功!";
    } else {
        NSLog(@"bundle load framework err:%@",err);
    }
    
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:str message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:@"確定", nil];
    [alertView show];
}

使用dlopen加載動態(tài)庫

以PiaoJinDylib.framework為例螃宙,動態(tài)庫中真正的可執(zhí)行代碼為PiaoJinDylib.framework/PiaoJinDylib文件蛮瞄,因此使用dlopen時指定加載動態(tài)庫的路徑為PiaoJinDylib.framework/PiaoJinDylib。

 NSString *documentsPath = [NSString stringWithFormat:@"%@/Documents/PiaoJinDylib.framework/PiaoJinDylib",NSHomeDirectory()]; 
[self dlopenLoadDylibWithPath:documentsPath];
    if (dlopen([path cStringUsingEncoding:NSUTF8StringEncoding], RTLD_NOW) == NULL) { 
        char *error = dlerror(); 
        NSLog(@"dlopen error: %s", error); 
    } else { 
        NSLog(@"dlopen load framework success."); 
 } 

調(diào)用動態(tài)庫中的方法

//調(diào)用FrameWork的方法,利用runTime運行時
- (IBAction)callMethodOfFrameWork:(id)sender {
    Class piaoJinClass = NSClassFromString(@"PiaoJin");
    if(piaoJinClass){
        //事先要知道有什么方法在這個FrameWork中
        id object = [[piaoJinClass alloc] init];
        //由于沒有引入相關(guān)頭文件故通過performSelector調(diào)用
        [object performSelector:@selector(love)];
    }else {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"調(diào)用方法失敗!" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:@"確定", nil];
        [alertView show];

    }
}

監(jiān)測動態(tài)庫的加載和移除

我們可以通過下述方式谆扎,為動態(tài)庫的加載和移除添加監(jiān)聽回調(diào):

+ (void)load 
{ 
  _dyld_register_func_for_add_image(&image_added); 
  _dyld_register_func_for_remove_image(&image_removed); 
} 

github上有一個完整的示例代碼挂捅。

后記

這邊只是一件最簡單的例子,實際項目中肯定需要與動態(tài)庫所代表的模塊進行交互,數(shù)據(jù)的共享等等,這些都是需要去根據(jù)實際項目場景再去設(shè)計解決的!

如果是真機調(diào)試可以通過運行需打開iTunes導入到PiaoJinFrameWorkDemo應(yīng)用中。

該Demo已經(jīng)上傳GitHub,有需要的碼友可以去看看

參考文檔:

WWDC2014之iOS使用動態(tài)庫

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末堂湖,一起剝皮案震驚了整個濱河市闲先,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌苗缩,老刑警劉巖饵蒂,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異酱讶,居然都是意外死亡退盯,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門泻肯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渊迁,“玉大人,你說我怎么就攤上這事灶挟×鹦啵” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵稚铣,是天一觀的道長箱叁。 經(jīng)常有香客問我,道長惕医,這世上最難降的妖魔是什么耕漱? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮抬伺,結(jié)果婚禮上螟够,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好妓笙,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布若河。 她就那樣靜靜地躺著,像睡著了一般寞宫。 火紅的嫁衣襯著肌膚如雪萧福。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天辈赋,我揣著相機與錄音统锤,去河邊找鬼。 笑死炭庙,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的煌寇。 我是一名探鬼主播焕蹄,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼阀溶!你這毒婦竟也來了腻脏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤银锻,失蹤者是張志新(化名)和其女友劉穎永品,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體击纬,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡鼎姐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了更振。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炕桨。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖肯腕,靈堂內(nèi)的尸體忽然破棺而出献宫,到底是詐尸還是另有隱情,我是刑警寧澤实撒,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布姊途,位于F島的核電站,受9級特大地震影響知态,放射性物質(zhì)發(fā)生泄漏捷兰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一肴甸、第九天 我趴在偏房一處隱蔽的房頂上張望寂殉。 院中可真熱鬧,春花似錦原在、人聲如沸友扰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽村怪。三九已至秽浇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間甚负,已是汗流浹背柬焕。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留梭域,地道東北人斑举。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像病涨,于是被迫代替她去往敵國和親富玷。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容