您的位置:首页 > 移动开发 > IOS开发

WWDC2014之iOS使用动态库2

2015-06-12 14:44 381 查看


2、通用动态库

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

创建Aggregate Target

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



按提示一步步操作即可,我给
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

添加的脚本为:

12
3
4
5
6
7
8
9
10
1112
13
14
15
16
17
18
19
20
2122
23
24
25
26
27
28
29
30
31

# 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}"
open "${INSTALL_DIR}"

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



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

脚本的主要功能是:

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

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




使用动态库


添加动态库到工程文件

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

1

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

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

1

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

如图所示:



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


自动链接动态库

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

1

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

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

1

@executable_path/

如图所示:



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


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


需要的时候链接动态库

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

更改设置

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

1

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

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

使用dlopen加载动态库

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

示例代码如下:

12
3
4
5
6
7
8
9
10
1112
13
14
15
16
17

- (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来加载动态库,实现代码如下:

12
3
4
5
6
7
8
9
10
1112
13
14
15
16
17

- (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
类的使用为例:

12
3
4
5
6
7
8

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

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

12
3
4
5
6
7

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


监测动态库的加载和移除

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

12
3
4
5

+ (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中静态库和动态库的区别和汇总

Posted by 王中周 Jun
12th, 2014 WWDC2014
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: