在xcode中创建和使用静态链接库
参考链接
4.Making your own iPhone Frameworks
6.How to Create a Framework for iOS
静态链接库与动态链接库
在iOS中只允许使用自定义的静态链接库(.a),不能使用动态链接库(.dylib)。官方提供的framework其实是动态链接库。从Xcode 6开始,可以创建自己的framework。越狱后安装的各种插件大部分是以dylib的形式存在的。
本文的目的
创建一个静态链接库。将它所使用的资源放在bundle中。
本文所使用的环境
Xcode7-beta3
创建静态链接库
1.选择模板iOS framework & library的cocoa touch static library,填写名称等……
创建完成后,可以看到工程目录下面有与工程同名的.h和.m文件。Products文件夹里面是红色的libxxx.a文件。如下图。
设置暴露的静态链接库头文件 和 私有的头文件
选中MyStaticLibrary这个Target,在Build Phases 里面,点击左上角的加号,选择“New Header Phase”,界面如下图。
- 在
Public
是暴露的对外可见的头文件 - 据raywenderlich上的这篇文章所说,在
Protect
里面放的是我们想要保护的私有头文件
例如,我们再创建一个UIViewController的子类,名为MyViewController。那么MyViewController.h应该放在Private
标签下面
在文件中添加代码,在xib里面添加控件
在MyStaticLibrary.h和.m里面添加两个方法,随便打印出一句话。
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface MyStaticLibrary : NSObject
-(void)printSomething;
-(UIViewController * )customViewController;
@end
#import "MyStaticLibrary.h"
#import "MyViewController.h"
@implementation MyStaticLibrary
-(void)printSomething
{
NSLog(@"---in MyStatic Library,print something---");
}
-(UIViewController *)customViewController
{
MyViewController * vc = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:[self frameworkBundle]];
return vc;
}
-(NSBundle *)frameworkBundle {
NSBundle* frameworkBundle = nil;
NSString* mainBundlePath = [[NSBundle mainBundle] resourcePath];
NSString* frameworkBundlePath = [mainBundlePath stringByAppendingPathComponent:@"MyResource.bundle"];
frameworkBundle = [NSBundle bundleWithPath:frameworkBundlePath];
NSLog(@"--get custom bundle done--");
return frameworkBundle;
}
@end
在MyViewController.xib文件里面添加一个UIImageView控件,给该控件添加IBOutlet,以便在代码中修改它的某些属性。
创建bundle这种类型的target,用于存放资源文件
选中Mac os x里面的bundle模板,创建一个用于存放资源文件的bundle,如下图所示。
会看见Products里面多了一个bundle,如下图所示。
由于创建的bundle是默认适用于Mac OS X的,所以要进行相关的改动。
打开bundle这个target的build settings,看到Architectures这个模块,如下图所示,它默认是用Max OS X的SDK编译的适用于x86_64和i386架构,但我们是要在iOS上使用它,应该改成使用iOS SDK编译,适用于ARM架构。
更改MyResource.bunlde这个target的build settings的COMBINE_HIDPI_IMAGE
这个参数为 NO,因为如果它为YES它会把图片变成TIFF。
在bundle这个target的build phrase里面的copy bundle resources里面添加上资源文件。
在静态链接库里面要使用bundle里面的资源的时候,代码如下。给刚刚创建的xib里面的UIImageView绑定一个图片。
#import "MyViewController.h"
@interface MyViewController ()
@property (unsafe_unretained, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.imageView.image = [UIImage imageNamed:@"MyResource.bundle/dog.jpeg"];
}
@end
踩过的几个坑
- 1.之前好奇为什么Products文件夹里面是红色的,去网上搜。说是因为还没有生成,所以是红色的。选中“iOS Device”之后run一下就变成黑色了,果真如此。但是生成的东西,是只适用于x86_64架构的,直接拖到iOS工程中使用的时候,会报错。【所以,应该要生成一个适用于各个架构的通用文件,这样才能够比较方便】
- 2.之前在网上搜索怎样生成bundle文件。很多博客或者帖子里面都说直接建一个文件夹,把资源文件拷贝进去,然后将文件夹的后缀名改成bundle就行了。但是由于我的资源文件里面有xib文件,它需要被编译成nib文件才能够被使用,但此种直接该后缀名的方法显然不能够编译,所以此法行不通。
生成通用库
File -> new -> target。选择other里面的Aggregate模板。如下图所示
给刚生成的target添加run script
在run script里面添加如下脚本
set -e
# If we're already inside this script then die
if [ -n "$RW_MULTIPLATFORM_BUILD_IN_PROGRESS" ]; then
exit 0
fi
export RW_MULTIPLATFORM_BUILD_IN_PROGRESS=1
RW_INPUT_STATIC_LIB="lib${PROJECT_NAME}.a"
function build_static_library {
# Will rebuild the static library as specified
# build_static_library sdk
xcrun xcodebuild -project "${PROJECT_FILE_PATH}" \
-target "${TARGET_NAME}" \
-configuration "${CONFIGURATION}" \
-sdk "${1}" \
ONLY_ACTIVE_ARCH=NO \
BUILD_DIR="${BUILD_DIR}" \
OBJROOT="${OBJROOT}" \
BUILD_ROOT="${BUILD_ROOT}" \
SYMROOT="${SYMROOT}" $ACTION
}
function make_fat_library {
# Will smash 2 static libs together
# make_fat_library in1 in2 out
xcrun lipo -create "${1}" "${2}" -output "${3}"
}
# 1 - Extract the platform (iphoneos/iphonesimulator) from the SDK name
if [[ "$SDK_NAME" =~ ([A-Za-z]+) ]]; then
RW_SDK_PLATFORM=${BASH_REMATCH[1]}
else
echo "Could not find platform name from SDK_NAME: $SDK_NAME"
exit 1
fi
# 2 - Extract the version from the SDK
if [[ "$SDK_NAME" =~ ([0-9]+.*$) ]]; then
RW_SDK_VERSION=${BASH_REMATCH[1]}
else
echo "Could not find sdk version from SDK_NAME: $SDK_NAME"
exit 1
fi
# 3 - Determine the other platform
if [ "$RW_SDK_PLATFORM" == "iphoneos" ]; then
RW_OTHER_PLATFORM=iphonesimulator
else
RW_OTHER_PLATFORM=iphoneos
fi
# 4 - Find the build directory
if [[ "$BUILT_PRODUCTS_DIR" =~ (.*)$RW_SDK_PLATFORM$ ]]; then
RW_OTHER_BUILT_PRODUCTS_DIR="${BASH_REMATCH[1]}${RW_OTHER_PLATFORM}"
else
echo "Could not find other platform build directory."
exit 1
fi
# Build the other platform.
build_static_library "${RW_OTHER_PLATFORM}${RW_SDK_VERSION}"
# If we're currently building for iphonesimulator, then need to rebuild
# to ensure that we get both i386 and x86_64
if [ "$RW_SDK_PLATFORM" == "iphonesimulator" ]; then
build_static_library "${SDK_NAME}"
fi
# Join the 2 static libs into 1 and push into the .framework
make_fat_library "${BUILT_PRODUCTS_DIR}/${RW_INPUT_STATIC_LIB}" \
"${RW_OTHER_BUILT_PRODUCTS_DIR}/${RW_INPUT_STATIC_LIB}" \
"${HOME}/Desktop/${RW_INPUT_STATIC_LIB}"
echo "make fat library done"
# Copy the resources bundle to the user's desktop
ditto "${BUILT_PRODUCTS_DIR}/MyResource.bundle" \
"${HOME}/Desktop/MyResource.bundle"
给刚生成的target添加target dependencies
然后选择“MyAggregate”,run一下,就会在桌面上看到静态库文件和bundle文件。
打开bundle文件看一下,可以看到里面的xib文件已经被编译成了nib文件。
查看静态链接库适用的架构
使用如下命令可以查看静态链接库适用的架构
lipo -info libXXX.a
在别的工程里面使用刚才的静态链接库和bundle
新建一个工程,取名为TestStaticAndBundle,将静态库文件、静态库头文件、bundle文件拖到新建的工程中。在ViewController.m里面添加如下代码,测试静态库里面的方法能否被调用。
#import "ViewController.h"
#import "MyStaticLibrary.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
MyStaticLibrary * mystat = [[MyStaticLibrary alloc] init];
[mystat printSomething];
UIViewController * vc = [mystat customViewController];
vc.view.frame = CGRectMake(20, 20, 200, 200);
[self.view addSubview:vc.view];
}
@end
运行程序,可以看到成功调用了静态链接库里面的方法。