WWDC2014之iOS使用动态库2

来源:互联网 发布:数据仿真 编辑:程序博客网 时间:2024/05/18 01:47

2、通用动态库

经过第一步我们只是创建了一个动态库文件,但是和静态库类似,该动态库并同时不支持真机和模拟器,可以通过以下步骤创建通用动态库:

  • 创建Aggregate Target

按下图所示,在动态库工程中添加一个类型为Aggregate的target:

aggregate

按提示一步步操作即可,我给Aggregate的Target的命名是CommonDylib

  • 设置Target Dependencies

按以下路径设置CommonDylib对应的Target Dependencies:

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

将真正的动态库Dylib Target添加到其中。

  • 添加Run Script

按以下路径为CommonDylib添加Run Script:

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

添加的脚本为:

12345678910111213141516171819202122232425262728293031
# 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=buildDEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.frameworkSIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework# -configuration ${CONFIGURATION} # Clean and Building both architectures.xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphoneos clean buildxcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator clean build# Cleaning the oldest.if [ -d "${INSTALL_DIR}" ]thenrm -rf "${INSTALL_DIR}"fimkdir -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}"open "${INSTALL_DIR}"

添加以后的效果如图所示:

commonlib_setting

该脚本是我根据一篇文章中介绍的脚本改写的,感谢原文作者。

脚本的主要功能是:

1.分别编译生成真机和模拟器使用的framework; 2.使用lipo命令将其合并成一个通用framework; 3.最后将生成的通用framework放置在工程根目录下新建的Products目录下。

如果一切顺利,对CommonDylib target执行run操作以后就能生成一个如图所示的通用framework文件了:

products

使用动态库

添加动态库到工程文件

经过以上步骤的努力,生成了最终需要的framework文件,为了演示动态库的使用,新建了一个名为FrameworkDemo的工程。通过以下方式将刚生成的framework添加到工程中:

1
Targets-->Build Phases-->Link Binary With Libraries

同时设置将framework作为资源文件拷贝到Bundle中:

1
Targets-->Build Phases-->Copy Bundle Resources

如图所示:

framework_demo_setting

仅仅这样做是不够的,还需要为动态库添加链接依赖。

自动链接动态库

添加完动态库后,如果希望动态库在软件启动时自动链接,可以通过以下方式设置动态库依赖路径:

1
Targets-->Build Setting-->Linking-->Runpath Search Paths

由于向工程中添加动态库时,将动态库设置了Copy Bundle Resources,因此就可以将Runpath Search Paths路径依赖设置为main bundle,即沙盒中的FrameworkDemo.app目录,向Runpath Search Paths中添加下述内容:

1
@executable_path/

如图所示:

run_search_path

其中的@executable_path/表示可执行文件所在路径,即沙盒中的.app目录,注意不要漏掉最后的/

如果你将动态库放到了沙盒中的其他目录,只需要添加对应路径的依赖就可以了。

需要的时候链接动态库

动态库的另一个重要特性就是即插即用性,我们可以选择在需要的时候再加载动态库。

  • 更改设置

如果不希望在软件一启动就加载动态库,需要将

1
Targets-->Build Phases-->Link Binary With Libraries

Dylib.framework对应的Status由默认的Required改成Optional;或者更干脆的,将Dylib.frameworkLink Binary With Libraries列表中删除即可。

  • 使用dlopen加载动态库

Dylib.framework为例,动态库中真正的可执行代码为Dylib.framework/Dylib文件,因此使用dlopen时如果仅仅指定加载动态库的路径为Dylib.framework是没法成功加载的。

示例代码如下:

1234567891011121314151617
- (IBAction)onDlopenLoadAtPathAction1:(id)sender{    NSString *documentsPath = [NSString stringWithFormat:@"%@/Documents/Dylib.framework/Dylib",NSHomeDirectory()];    [self dlopenLoadDylibWithPath:documentsPath];}- (void)dlopenLoadDylibWithPath:(NSString *)path{    libHandle = NULL;    libHandle = dlopen([path cStringUsingEncoding:NSUTF8StringEncoding], RTLD_NOW);    if (libHandle == NULL) {        char *error = dlerror();        NSLog(@"dlopen error: %s", error);    } else {        NSLog(@"dlopen load framework success.");    }}

以dlopen方式使用动态库不知道是否能通过苹果审核。

  • 使用NSBundle加载动态库

也可以使用NSBundle来加载动态库,实现代码如下:

1234567891011121314151617
- (IBAction)onBundleLoadAtPathAction1:(id)sender{    NSString *documentsPath = [NSString stringWithFormat:@"%@/Documents/Dylib.framework",NSHomeDirectory()];    [self bundleLoadDylibWithPath:documentsPath];}- (void)bundleLoadDylibWithPath:(NSString *)path{    _libPath = path;    NSError *err = nil;    NSBundle *bundle = [NSBundle bundleWithPath:path];    if ([bundle loadAndReturnError:&err]) {        NSLog(@"bundle load framework success.");    } else {        NSLog(@"bundle load framework err:%@",err);    }}

使用动态库中代码

通过上述任一一种方式加载的动态库后,就可以使用动态库中的代码文件了,以Dylib.framework中的Person类的使用为例:

12345678
- (IBAction)onTriggerButtonAction:(id)sender{    Class rootClass = NSClassFromString(@"Person");    if (rootClass) {        id object = [[rootClass alloc] init];        [(Person *)object run];    }}

注意,如果直接通过下属方式初始化Person类是不成功的:

1234567
- (IBAction)onTriggerButtonAction:(id)sender{    Person *object = [[Person alloc] init];    if (object) {       [object run];    }}

监测动态库的加载和移除

我们可以通过下述方式,为动态库的加载和移除添加监听回调:

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

github上有一个完整的示例代码,

从这里看出,原来就算空白工程软件启动的时候也会加载多达一百二十多个动态库,如果这些都是静态库,那该有多可怕!!

Demo

本文使用的例子已经上传到github上,需要的朋友请自取。

另外,本文对某些东西可能有理解错误的地方,还请指出。

参考文档:

  • Framework Programming Guide

  • OS X Man Pages

  • New Features in Xcode 6 Beta

  • ImageLogger

  • Dynamic Linking

  • Dynamic loading

  • Integrating Reveal with your iOS app

  • IOS Framework制作全攻略

  • Build Settings中的变量@rpath,@loader_path,@executable_path

  • 深入浅出Cocoa之Framework

  • linux中静态库和动态库的区别和汇总

 Jun 12th, 2014  WWDC2014

0 0
原创粉丝点击