1. 程式人生 > >IOS-使用framework實現功能模組動態更新

IOS-使用framework實現功能模組動態更新

測試Xcode版本為6.1.1

實現過程簡述:把想要的作為動態更新的模組,移動到我們建立的Framework工程中,然後得到我們想要的動態庫檔案,把此檔案再通過iTunes放到“主程式”專案的document資料夾下,從而實現從主程式中去載入此動態庫,從而實現功能模組的動態更新效果。理想效果為支付寶APP,而支付寶採用的是HTML5(網頁)的形式實現的,這是目前最通用的實現方式,但是不適用於複雜介面效果。

第一步:建立IOS Framework工程



第二步:生成的工程中原本的類刪掉不用


然後新增新的類檔案來實現動態載入效果



圖片和測試介面不多說,重點為PacteraFramework這個類檔案中的程式碼部分:

新增入口方法:

/*
 主程式和此動態庫的關係樞紐,也就是從“主程式”到“動態庫內封裝的程式”的入口方法
 @param mainCon “主程式”中入口按鈕所在的ViewController物件
 @param bundle 此動態庫在document檔案中的路徑,用於xib的載入和圖片的載入
 */
-(void)showView:(id)mainCon withBundle:(NSBundle *)bundle;

實現此方法:
-(void)showView:(id)mainCon withBundle:(NSBundle *)bundle
{
    /*
     *初始化第一個controller
      TheFirstViewController繼承於RootViewController
     *這裡的重點是xib檔案的載入
      通常我們在初始化xib的時候並不是很在意bundle:這個引數,一般情況下都會賦予nil值
      其實我們所用到的圖片、xib等資原始檔都是在程式內部中獲取的,也就是我們常用的[NSBundle mainBundle]中獲取,所謂的NSBundle本質上就是一個路徑,mainBundle指向的是.app下。
      而如果我們不指定bundle,則會預設從.app路徑下去尋找資源。
      不過很顯然,我們的動態庫是放到“主程式”的document檔案下的,所以資原始檔是不可能在[NSbundle mainBundle]中獲取到的,所以這裡我們需要指定bundle引數,這也是傳遞framework的路徑的意義所在
     */
    TheFirstViewController *firstCon = [[TheFirstViewController alloc]initWithNibName:@"TheFirstViewController" bundle:bundle];
    //儲存NSBundle
    firstCon.root_bundle = bundle;
    
    //加上導航欄,並隱藏。
    UINavigationController *navCon = [[UINavigationController alloc]initWithRootViewController:firstCon];
    [navCon setNavigationBarHidden:YES];
    
    //轉換傳遞過來的mainCon引數,實現介面跳轉
    UIViewController *viewCon = (UIViewController *)mainCon;
    [viewCon presentViewController:navCon animated:YES completion:^{
        NSLog(@"跳轉到動態更新模組成功!");
    }];
}

上面描述了xib檔案的載入,下面是使用圖片的注意事項:(使用RootViewController基類的程式碼做說明)
/*
     *注意獲取圖片的方式,通過路徑+圖片名稱去獲取
     *如果直接使用[UIImage imageNamed:@"root_top_bg.png"]方式載入是會出現問題,
      因為在當前程式的路徑下是找不到此圖片的,圖片也會被封裝到framwork中
     *如果在xib檔案中直接為某個控制元件新增圖片的話,和平常使用一樣直接新增圖片名稱,不會出現路徑的問題
     */
    [navImageView setImage:[UIImage imageWithContentsOfFile:[self.root_bundle pathForResource:@"root_top_bg" ofType:@"png"]]];

以上為一些frameweok工程中的注意事項,現在我們執行得到動態庫檔案



選擇Show in Finder,取出framwork檔案


第三步:建立“主程式”的專案工程


第四步:設定此工程可以通過iTunes來實現檔案共享



然後通過itunes把framework放到document路徑中


第五步:獲取framework,並呼叫上面提到的動態庫入口方法和傳遞引數

(此專案在介面上添加了一個簡單的按鈕,點選按鈕來進入動態庫)

-(void)testFramework
{
    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
    NSString *documentDirectory = nil;
    if ([paths count] != 0)
        documentDirectory = [paths objectAtIndex:0];
    
    //拼接我們放到document中的framework路徑
    NSString *libName = @"PacteraFramework.framework";
    NSString *destLibPath = [documentDirectory stringByAppendingPathComponent:libName];
    
    //判斷一下有沒有這個檔案的存在 如果沒有直接跳出
    NSFileManager *manager = [NSFileManager defaultManager];
    if (![manager fileExistsAtPath:destLibPath]) {
        NSLog(@"There isn't have the file");
        return;
    }
    
    //複製到程式中
    NSError *error = nil;
    
    //載入方式一:使用dlopen載入動態庫的形式 使用此種方法的時候注意標頭檔案的引入
//    void* lib_handle = dlopen([destLibPath cStringUsingEncoding:NSUTF8StringEncoding], RTLD_LOCAL);
//    if (!lib_handle) {
//        NSLog(@"Unable to open library: %s\n", dlerror());
//        return;
//    }
    //載入方式一 關閉的方法
    // Close the library.
//    if (dlclose(lib_handle) != 0) {
//        NSLog(@"Unable to close library: %s\n",dlerror());
//    }
    
    //載入方式二:使用NSBundle載入動態庫
    NSBundle *frameworkBundle = [NSBundle bundleWithPath:destLibPath];
    if (frameworkBundle && [frameworkBundle load]) {
        NSLog(@"bundle load framework success.");
    }else {
        NSLog(@"bundle load framework err:%@",error);
        return;
    }
    
    /*
     *通過NSClassFromString方式讀取類
     *PacteraFramework 為動態庫中入口類
     */
    Class pacteraClass = NSClassFromString(@"PacteraFramework");
    if (!pacteraClass) {
        NSLog(@"Unable to get TestDylib class");
        return;
    }
    
    /*
     *初始化方式採用下面的形式
      alloc init的形式是行不通的
      同樣,直接使用PacteraFramework類初始化也是不正確的
     *通過- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
      方法呼叫入口方法(showView:withBundle:),並傳遞引數(withObject:self withObject:frameworkBundle)
     */
    NSObject *pacteraObject = [pacteraClass new];
    [pacteraObject performSelector:@selector(showView:withBundle:) withObject:self withObject:frameworkBundle];
    
}

第六步:執行“主程式”專案到裝置上,然後點選按鈕進入動態庫模組吧。

注意:有時候我們執行framework工程獲取framework檔案,內部並沒有我們想要的xib檔案(這個時候內部展示的為nib格式的),所以在我們每次執行此工程的時候,都需要clean