Osheep

时光不回头,当下最重要。

iOS - 动态库及静态库的使用

开篇

想要构建一个高逼格的应用程序,库的应用必不可少,同时想要真正进入iOS开发的世界”库“是我们要掌握的。

《iOS - 动态库及静态库的使用》

那儿_并不远

iOS中库的相关概念

  • :就是一段已经编译好的二进制代码,加上头文件就可以提供给其他人使用了。iOS中常见的有 .a,.dylib,.Framework等结尾的库。

  • 库的分类 :开源库和闭源库
    (1)开源库 :比如我们常用的AFNetworking,源码都是开发给我们开发者的,下载后copy到项目中可以直接使用。在开发中我们常用的是cocoapods,用命令直接导入开源库,不用关心开源库的相关依赖。其实cocoapods的本质是将开源库编译成静态库,然后使得主工程依赖此静态库。
    (2)闭源库 :可以分为静态库和动态库,像我们开发时用到的百度地图等三方框架就会用到很多闭源库。

  • 深入理解静态库和动态库
    (1)静态库 :它是你的.m文件编译而成的二进制文件集合,需要配合暴漏在外面的.h文件使用,在程序编译的时候会被直接copy到目标程序中,在编译完成之后,库文件实际就没有多大作用了,目标程序没有外部依赖,可以直接运行,所以缺点就是导致目标程序的体积增大。
    (2)动态库 :它也是你的.m文件编译而成的二进制文件集合,需要配合暴漏在外面的.h文件使用,但是不同的是,在编译时动态库不会被copy到目标程序中,目标程序只会存储指向动态库的引用,等到程序运行时,动态库才会被真正加载进来。不会影响目标程序的体积,但是会带来一部分性能损失。而且目前苹果不允许自制的动态库上架到AppStore

  • 使用静态库和动态库的好处
    (1)静态库 :模块化,可以避免少量改动经常导致大量的重复编译连接,也可以重用,但注意不是共享使用。
    (2)动态库 :可以将最终的可执行文件体积缩小,多个应用程序共享内存中的同一份库文件,达到节约资源的效果,也可以不重新编译连接可执行程序的前提下,更新动态库文件达到更新应用程序的目的。但是可悲的是苹果不允许自制的动态库上架到AppStore,但是可以开发公司内部的应用程序还是比较方便的。

创建静态库和动态库

下面我们就先分别创建静态库和动态库,然后将我们创建的静态库和动态库应用到我们的项目中,这样便于大家更好的理解静态库和动态库,也方便大家更好的构建自己的项目或框架


说明 :我写一个小Demo,Demo的功能是改变UIImage的颜色,我会分别创建静态库和动态库来实现不同的功能模块(当然,都可以创建为静态库,但是为了让大家更清楚两种库的创建步骤)
(1)静态库:封装资源文件(.bundle文件)用来存放图片,封装返回图片路径的方法并提供外界 .h 文件
(2)动态库:封装改变UIimage的颜色的方法并提供外界 .h 文件。

顺便说一下Bundle文件,Bundle文件是静态的,也就是说,我们包含到包中的资源文件作为一个资源包是不参加项目编译的。也就意味着,bundle包中不能包含可执行的文件。它仅仅是作为资源,被解析成为特定的2进制数据。


  • (1)创建静态库
    第一步:打开Xcode,会弹出下面的选择框,然后大家选择标记处,然后为静态库命名并创建。
《iOS - 动态库及静态库的使用》

(1)

创建成功后大家可以看到,Products下的libStaticLibraryTest.a文件是红色的,不要方,command+B编译或者run一下就好了,静态库是程序编译是copy到目标程序中的。如下图,系统已经为我们创建好了一个以工程名命名的 .h 和 .m 文件。你可以用也可以不用,我就直接用了

《iOS - 动态库及静态库的使用》

(2)

第二步:创建一个Bundle文件,command+N就会弹出下面的选择框,然后选择标记并命名创建
![Uploading 屏幕快照 2016-11-17 上午1.06.21_068118.png . . .]

《iOS - 动态库及静态库的使用》

(3)

如下图,Bundle文件创建成功后,我向其中放入一张图片

《iOS - 动态库及静态库的使用》

(4)

第三步:添加Bundle文件到Copy File中,targets -> build phases -> copy files,如下图所示

《iOS - 动态库及静态库的使用》

(5)

第四步:写相关方法

(1)在StaticLibraryTest.h提供对外方法接口

#import <Foundation/Foundation.h>

@interface StaticLibraryTest : NSObject

+ (NSString *)getImagePathWithBundleName:(NSString *)bundleName ImageName:(NSString *)imageName Type:(NSString *)type;

@end

(2)在StaticLibraryTest.m中实现方法

#import "StaticLibraryTest.h"

@implementation StaticLibraryTest

+ (NSString *)getImagePathWithBundleName:(NSString *)bundleName ImageName:(NSString *)imageName Type:(NSString *)type {

    NSString *bundlePath = [[NSBundle mainBundle] pathForResource:bundleName ofType:@"bundle"];
    NSString *path = [NSString stringWithFormat:@"%@/%@.%@", bundlePath, imageName, type];
    return path;

}

@end

第五步:执行程序,command+R,这样我们的静态库就生成好了,点击libStaticLibraryTest.a,然后show in fender,我们会看到下面我们生成了针对于模拟器的静态库,注意我们command+R是在模拟器中run的,如果想生成针对真机的静态库,必须在真机下run,然后可以通过lipo -create -output命令在终端合并两个静态库文件,这样我们就得到一个既可以在真机又可以在模拟器上使用的静态库了(这里不多说了)。应用时我们就将标记处copy到我们的项目中,就可以直接使用了

《iOS - 动态库及静态库的使用》

(6)
  • (2)创建动态库

第一步:打开Xcode,会弹出下面的选择框,然后大家选择标记处,然后为静态库命名并创建。

《iOS - 动态库及静态库的使用》

(7)

创建完成后如图所示

《iOS - 动态库及静态库的使用》

(8)

第二步:创建一个ChangeImageColor类,用来写改变图片颜色的方法

ChangeImageColor.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface ChangeImageColor : NSObject

+ (UIImage *)changeImageColorWithColor:(UIColor *)color Image:(UIImage *)image;

@end

ChangeImageColor.m

#import "ChangeImageColor.h"

@implementation ChangeImageColor

+ (UIImage *)changeImageColorWithColor:(UIColor *)color Image:(UIImage *)image
{

    UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(context, 0, image.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
    CGContextClipToMask(context, rect, image.CGImage);
    [color setFill];
    CGContextFillRect(context, rect);
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;

}

@end

第三步:设置开放的头文件可以通过target—>build phases—>headers来设置,如下图

《iOS - 动态库及静态库的使用》

(9)

同时可以在DynamicLibraryTest.h中倒入自己创建类的头文件

《iOS - 动态库及静态库的使用》

(10)

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

  • 创建Aggregate,并命名创建。
《iOS - 动态库及静态库的使用》

(11)
  • 设置Target Dependencies,targets -> Common -> Build Phases -> Target Dependencies,将动态库添加到Target Dependencies中
《iOS - 动态库及静态库的使用》

(12)
  • 添加Run Script
《iOS - 动态库及静态库的使用》

(13)
  • 添加脚本,复制粘贴就好
# Sets the target folders and the final framework product.
# 如果工程名称和Framework的Target名称不一样的话,要自定义FMK_NAME,否则不需要改动
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}"

脚本添加完后,command+B执行程序即可,在show in finder找到我们生成的动态库,如图,同样我也只生成了模拟器的动态库,和静态库原理相同,可通过终端来合成真机和模拟器共用的动态库

《iOS - 动态库及静态库的使用》

(14)

应用静态库和动态库

通过以上步骤我们已经分别生成了静态库和动态库,接下来我们就开始运用了,很简单,一步一步来就好.

  • 第一步:创建一个工程,这里多说了。
  • 第二部:将静态库和动态库copy到工程中,添加后如图所示
《iOS - 动态库及静态库的使用》

(15)

但是这时候需要注意了,静态库copy到文件中可以直接使用,但是对动态库必须添加依赖路径

  • 第三步:添加完动态库后,同时设置将framework作为资源文件拷贝到Bundle中,Targets–>Build Phases–>Copy Bundle Resources,如下图所示,
《iOS - 动态库及静态库的使用》

(16)

好了现在我们就将静态库和动态库copy到了我们的工程中,这时我们就可以使用了。

项目实战

接下来我在工程中的 ViewController.m 文件中代码如下

#import "ViewController.h"

// 引入静态库中文件
#import "StaticLibraryTest.h"

// 引入动态库
#import <DynamicLibraryTest/DynamicLibraryTest.h>

@interface ViewController ()

@property (nonatomic, strong) UIImageView *imageOfTest;

@property (nonatomic, strong) UIButton *button;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    self.view.backgroundColor = [UIColor whiteColor];

    [self createSubView];

}



- (void)createSubView {


    self.imageOfTest = [[UIImageView alloc] initWithFrame:CGRectMake((self.view.frame.size.width - 50) / 2, 200, 50, 50)];

    // 静态库中提供的方法
    NSString *imagePath = [StaticLibraryTest getImagePathWithBundleName:@"Resource" ImageName:@"advantage" Type:@"png"];
    self.imageOfTest.image = [UIImage imageWithContentsOfFile:imagePath];

    [self.view addSubview:self.imageOfTest];

    self.button = [[UIButton alloc] initWithFrame:CGRectMake(20, 450, self.view.frame.size.width - 40, 50)];
    self.button.backgroundColor = [UIColor cyanColor];
    [self.button setTitle:@"change color" forState:UIControlStateNormal];
    [self.view addSubview:self.button];
    [self.button addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];


}

- (void)buttonAction:(UIButton *)button {

    NSLog(@"改变颜色");

    UIImage *image = nil;
    // 利用动态库中提供方法实现
    if (button.selected == NO) {
        image = [ChangeImageColor changeImageColorWithColor:[UIColor blueColor] Image:self.imageOfTest.image];
    }
    else {
        image = [ChangeImageColor changeImageColorWithColor:[UIColor blackColor] Image:self.imageOfTest.image];
    }
    self.imageOfTest.image = image;

    button.selected = !button.selected;

}

总结

这次博客写的长了点,也许不会有太好的阅读效果,本来想拆分着写了,但是总感觉那样对技术的提高不算大。希望大家谅解。

点赞