在xcode中创建和使用静态链接库

参考链接

1.Xcode 6制作动态及静态Framework

2.在framework中打包xib

3.WWDC2014之iOS使用动态库

4.Making your own iPhone Frameworks

5.在framework或子工程中使用xib

6.How to Create a Framework for iOS

7.给iOS工程增加Daily Build

8.xcodebuild命令详解


静态链接库与动态链接库

在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标签下面

创建UIViewController的子类图片

暴露的头文件和私有的头文件图片

在文件中添加代码,在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,以便在代码中修改它的某些属性。

xib图片

创建bundle这种类型的target,用于存放资源文件

选中Mac os x里面的bundle模板,创建一个用于存放资源文件的bundle,如下图所示。

选择bundle模板

会看见Products里面多了一个bundle,如下图所示。

products图片

由于创建的bundle是默认适用于Mac OS X的,所以要进行相关的改动。
打开bundle这个target的build settings,看到Architectures这个模块,如下图所示,它默认是用Max OS X的SDK编译的适用于x86_64和i386架构,但我们是要在iOS上使用它,应该改成使用iOS SDK编译,适用于ARM架构。

默认使用Max OS X的SDK编译

改成使用iOS SDK编译

更改MyResource.bunlde这个target的build settings的COMBINE_HIDPI_IMAGE这个参数为 NO,因为如果它为YES它会把图片变成TIFF。

COMBINE_HIDPI_IMAGE

在bundle这个target的build phrase里面的copy bundle resources里面添加上资源文件。

target copy bundle resource

在静态链接库里面要使用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模板。如下图所示

aggregate

给刚生成的target添加run script
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
target dependencies

然后选择“MyAggregate”,run一下,就会在桌面上看到静态库文件和bundle文件。

static library and bundle

打开bundle文件看一下,可以看到里面的xib文件已经被编译成了nib文件。
nib

查看静态链接库适用的架构

使用如下命令可以查看静态链接库适用的架构

lipo -info libXXX.a

see static library cpu

在别的工程里面使用刚才的静态链接库和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

运行程序,可以看到成功调用了静态链接库里面的方法。
success show uiview in staticlibrary
success call the function in staticLibrary