- 浏览: 659545 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
lizaochengwen:
网络请求碰到的中文乱码使用encodeURL吧- (NSStr ...
iPhone开发/iPad开发 中文乱码问题 -
hhb19900618:
还是没弄懂怎么解决了中文乱码? 正确代码能重写贴出吗
iPhone开发/iPad开发 中文乱码问题 -
zhengjj_2009:
我的理解是讲ipa文件解压缩之后再重新打包,已经破坏了签名,所 ...
xcodebuild和xcrun实现自动打包iOS应用程序 -
zhengjj_2009:
我参考你的“ 从ipa格式的母包生成其它渠道包的shell脚本 ...
xcodebuild和xcrun实现自动打包iOS应用程序 -
同一片天空:
问题果然解决了
iOS 搭建 XMPP实现环境
作者:shede333
主页:http://my.oschina.net/shede333
版权声明:原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | [Creative Commons BY-NC-ND 3.0][]
本人英语也不是太好,翻译质量不是太高,如有不妥之处,欢迎指点批评。
点此查看文章 英文原文
创建IOS静态库
如果你开发ios有一段时间了,你可能有许多想在你大部分项目里重用的类和工具函数。
重用代码最容易的方法就是复制/黏贴,但是,这在代码维护上很快就变成一个噩梦。
既然每一个app都拥有一份 共享代码 的拷贝,这就很难保证所有 拷贝的代码 与 共享代码 在bug修正与更新的同步(一致性)。
这里就使用静态库来拯救噩梦。静态库就是类、函数、定义(definitions)和资源的一个包,使用静态库,你能把代码打包在一起,并且在你所有的项目间共享。
在这个教程,你将亲身经历使用两种不同的方法创建你自己的通用静态库。
你应该熟悉Objective-C and iOS开发,才能理解大致上这个教程。
如果你对怎样做一个相同的app 以及 图像滤光代码在库里的工作原理 感兴趣,Core Image的相关知识虽然不是必须的,但是对你会很有帮助。
准备开始高效的减少、重用和循环使用你的代码吧!
为什么使用静态库
你可能因为很多原因而创建静态库:
你想要把你或者你团队里的成员编写的类打包在一起,便于合理的使用,并且很容易与周围的人分享。
你想要让所有通用的代码集中到一起,便于你对代码的bug修复与更新。
你想和一些人分享你的代码库,但是你不想要他们看到你的代码。
随着开发时间的进展,你想要做一个版本库的快照。
在这个教程里,假设你已经看过Core Image Tutorial这个教程,并且理解了如何使用一些照片处理效果的代码。
你将会把那些代码添加到静态库里,并且在一个修改过的app里使用静态库。最终将会得到一个相同的app,但是会体现出面陈提到的所有优点。
开始 Let`s Go!
打开Xcode,选择File\New\Project,当Choose a template对话框出现时,选择iOS\Framework & Library\Cocoa Touch Static Library,如下图:
点击Next,在项目选项对话框,输入ImageFliters作为项目名称(project name),然后输入一个唯一的公司标识(company identifier ),并且勾选Use Automatic Reference Counting,而Include Unit Tests不要勾选。
点击Next,最后选择一个你想要保存项目的位置,然后点击Create。
Xcode已经创建了一个准备使用静态库的项目,并且项目里已经自动添加了ImageFliters类。(这难道不是Xcode的优点么?)这就是你将要编写的图像滤光代码的地方。
1
注意:你可以你想要的任何类到静态库里,甚至删除原来的代码(ImageFliters类)。
2
在这个教程,所有代码将会添加到Xcode开始自带的类ImageFliters。
既然你项目仍然空,让我们添加一些代码进去吧!
项目:Image Filters
这个库是为ios设计的,并且使用了UIKit框架,所以,你要做的第一件事是:在头文件,导入(import)UIKit框架,打开ImageFilters.h,并且在该文件最顶部添加下列代码:
1
#import <UIKit/UIKit.h>
接下来,在文件@interface ImageFilters : NSObject这一行的下面,黏贴如下声明代码:
1
@property (nonatomic,readonly) UIImage *originalImage;
2
3
- (id)initWithImage:(UIImage *)image;
4
- (UIImage *)grayScaleImage;
5
- (UIImage *)oldImageWithIntensity:(CGFloat)level;
这些头文件的声明代码,定义了类的公开接口。当其他开发者(包括你自己)使用这个库,通过读这个头文件,他们就知道类名和内嵌的库中的方法。
现在,是时候添加实现代码(implementation)了,
打开ImageFilters.m,在这行#import "ImageFilters.h"下面黏贴如下代码:
@interface ImageFilters()
@property (nonatomic,strong) CIContext context;
@property (nonatomic,strong) CIImage beginImage;
@end
上面的代码声明了许多用于类内部的属性,这些属性并不是公开的,所以任何使用这个库的app都不能访问这些属性。
最后,你需要实现类中的方法,在这行@implementation ImageFilters下面黏贴如下代码:
01
- (id)initWithImage:(UIImage *)image
02
{
03
self = [super init];
04
if (self) {
05
_originalImage = image;
06
_context = [CIContext contextWithOptions:nil];
07
_beginImage = [[CIImage alloc] initWithImage:_ originalImage];
08
}
09
return self;
10
}
11
12
- (UIImage*)imageWithCIImage:(CIImage *)ciImage
13
{
14
CGImageRef cgiImage = [self.context createCGImage:ciImage fromRect:ciImage.extent];
15
UIImage *image = [UIImage imageWithCGImage:cgiImage];
16
CGImageRelease(cgiImage);
17
18
return image;
19
}
20
21
- (UIImage *)grayScaleImage
22
{
23
if( !self.originalImage)
24
return nil;
25
26
CIImage *grayScaleFilter = [CIFilter filterWithName:@"CIColorControls" keysAndValues:kCIInputImageKey, self.beginImage, @"inputBrightness", [NSNumber numberWithFloat:0.0], @"inputContrast", [NSNumber numberWithFloat:1.1], @"inputSaturation", [NSNumber numberWithFloat:0.0], nil] outputImage;
27
28
CIImage *output = [CIFilter filterWithName:@"CIExposureAdjust" keysAndValues:kCIInputImageKey, grayScaleFilter, @"inputEV", NSNumber numberWithFloat:0.7], nil].outputImage;
29
30
UIImage *filteredImage = [self imageWithCIImage:output];
31
return filteredImage;
32
}
33
34
- (UIImage *)oldImageWithIntensity:(CGFloat)intensity
35
{
36
if( !self.originalImage )
37
return nil;
38
39
CIFilter *sepia = [CIFilter filterWithName:@"CISepiaTone"];
40
[sepia setValue:self.beginImage forKey:kCIInputImageKey];
41
[sepia setValue:@(intensity) forKey:@"inputIntensity"];
42
43
CIFilter *random = [CIFilter filterWithName:@"CIRandomGenerator" ;
44
45
CIFilter *lighten = [CIFilter filterWithName:@"CIColorControls"];
46
[lighten setValue:random.outputImage forKey:kCIInputImageKey];
47
[lighten setValue:@(1 - intensity) forKey:@"inputBrightness"];
48
[lighten setValue:@0.0 forKey:@"inputSaturation"];
49
50
CIImage *croppedImage = [lighten.outputImage imageByCroppingToRect:[self.beginImage extent]];
51
52
CIFilter *composite = [CIFilter filterWithName:@"CIHardLightBlendMode"];
53
[composite setValue:sepia.outputImage forKey:kCIInputImageKey];
54
[composite setValue:croppedImage forKey:kCIInputBackgroundImageKey];
55
56
CIFilter *vignette = [CIFilter filterWithName:@"CIVignette"];
57
[vignette setValue:composite.outputImage forKey:kCIInputImageKey ;
58
[vignette setValue:@(intensity * 2) forKey:@"inputIntensity"];
59
[vignette setValue:@(intensity * 30) forKey:@"inputRadius"];
60
61
UIImage *filteredImage = [self imageWithCIImage:vignette outputImage];
62
63
return filteredImage;
64
}
这上面代码实现了初始化和执行Core Image滤光效果的方法。
因为解释上面Core Image代码的功能,超出本教程的范围,
所以,您能在这个Core Image Tutorial教程里,学习Core Image和滤光的相关知识。
此时,你的静态库拥有ImageFilters类,这个类公开如下3个方法:
initWithImage :初始化类
grayScaleImage :创建图片的灰度效果
oldImageWithIntensity :创建图片的陈旧效果
现在编译并且运行你的库(将Run按钮旁边的编译目标改为iOS Device,这样便于后面找到.a文件),
你将会注意到,点击Xcode的“Run”按钮,只执行编译;
实际上,你不能通过运行你的静态库来看到结果,因为还没有app支持它。
静态库使用.a作为扩展名,而不是.app、.ipa等。
生成的静态库在Xcode导航栏的Products文件夹里,
右击 或者 “按住CTRL,单击” libImageFilters.a ,在弹出的菜单里选择Show in Finder,如下图:
Xcode将会在iFinder打开文件夹,你会看到如下:
一个共享库的最终最终产品有2个部分:
头文件(Header files):在include文件夹里,你找到静态库的所有公开头文件(.h文件)。
因为你只有1个公共类,所以这个文件夹只有单独的ImageFilters.h文件。
你在之后的app项目里面,为了让Xcode在编译时知道 输出类(the exported classes),需要用到这个头文件。
二进制库文件(Binary Library):Xcode生成的静态库文件是ImageFilters.a。当你想要在里使用这个库的时候,你需要使用这个文件来连接静态库。
导入新框架(framework)到app,与导入静态库很相似。
你只需要简单的导入框架头文件(framework header)和连接到app的框架代码(framework code)。
静态库使用小警告:默认情况下,编译生成的库文件仅仅适用于Xcode当前指定的架构(真机的框架为ARM,模拟器框架为i386)。
假如你目前的编译目标为模拟器,库文件将包含适用于i386架构的对象代码;
假如你目前的编译目标为真机设备,库文件将包含适用于ARM架构的对象代码;
你需要编译出两个版本的静态库文件,当你的编译目标在真机和模拟器间切换时,要连接使用对应的库文件。
那要怎么做才能解决上面的问题呢?
幸运的是,有一个很好的办法,
app项目不需要创建多个配置或者最终产品(build products),就能支持多个平台。
您可以创建一个universal binary,它包含这两个架构的对象代码。
Universal Binaries(通用二进制)
通用二进制 是一种包含多个架构对象代码的特殊二进制文件。
在Mac电脑的cpu从PowerPC (PPC) 过渡到 Intel (i386) 时,你对 通用二进制 可能会熟悉。
在过渡期间,Mac上的app通常装载了通用二进制,通用二进制在一个二进制文件内包含了两个平台的可执行代码,以便app在 Intel 和 PowerPC 架构的Mac电脑上均可运行。
支持ARM和i386架构的概念并没有太多区别。既然这样,静态库文件将包含适用于ios设备(ARM)和模拟器(i386)代码。Xcode将会识别通用二进制,并且你没戏编译app时,Xcode会根据当前的编译目标,选择适当的架构。
为了创建通用二进制库,你需要使用一个系统工具lipo。
不用担心小猫咪,lipo并不是指的上图中的脂肪(这句话属于美式幽默,我也不太懂)。
lipo 是一种允许你在通用二进制文件进行操作(操作包括:创建通用二进制文件、显示文件内容等等)的命令行工具。
在这个教程里,你将会使用lipo,将不同架构的二进制文件 合并成 包含多个架构的内容的单独二进制文件。
你能在命令行里直接使用lipo,但是在这个教程里面,你将会通过运行 一个命令行脚本来让Xcode为你工作,这个命令行能创建通用二进制文件。
在Xcode,一个聚合目标(An Aggregate Target)将会一次编译多个目标(target),包括命令行脚本。
在Xcode菜单栏里操作File/New/Target,出现如下对话框,选择 iOS/Other,再点击Aggregate,如下图
在 Product Name 输入UniversalLib,确保Project栏选中ImageFilters项目,如下图
在项目导航栏上点击ImageFilters(如下图操作1),然后选择UniversalLib目标(如下图操作2),切换到到Build Phases标签(如下图操作3),
这里就是你将创建行为的地方,当目标被编译时,这里的行为将会运行。
点击右下角Add Build Phase按钮,在弹出菜单里选择Add Run Script,如下图:
现在你要创建一个脚本。展开Run Script模块,将下面的shell代码黏贴进去。
01
# define output folder environment variable
02
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
03
04
# Step 1. Build Device and Simulator versions
05
xcodebuild -target ImageFilters ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ ROOT="${BUILD_ROOT}"
06
xcodebuild -target ImageFilters -configuration ${CONFIGURATION} -sdk iphonesimulator -arch i386 BUILD_DIR="${BUILD_DIR}" BUILD_ ROOT="${BUILD_ROOT}"
07
08
# make sure the output directory exists
09
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
10
11
# Step 2. Create universal binary file using lipo
12
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/lib${PROJECT_NAME}.a"
13
14
# Last touch. copy the header files. Just for convenience
15
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/include" "${UNIVERSAL_ OUTPUTFOLDER}/"
上面的代码并不复杂,下面将逐行解释代码如何运行:
UNIVERSAL_OUTPUTFOLDER :通用二进制文件“Debug-universal”将被拷贝到的目标文件夹的路径名称。
Step 1 代码的第二行,你将调用xcodebuild,并且指导它编译ARM架构二进制文件(你将会在这行看到参数-sdk iphoneos)。
下一行代码再次调用xcodebuild,并且在另一个文件夹为iPhone模拟器编译生成i386架构二进制文件,但这次的参数为-sdk iphonesimulator -arch i386。
(假如你对此很感兴趣,你您能在man page学习更多xcodebuild的相关知识)
Step 2 现在你已经拥有分别针对两种架构的两个.a静态库文件,你只要调用 lipo -create 并且设置它创建、输出一个通用二进制文件。
在最后一行,你将头文件拷贝到 用于生成通用二进制的文件夹内(使用cpshell命令行)。
你的“Run Script ”窗口应该如下图一样:
现在你要准备去编译通用版本的静态库。在策略选择下拉表,选择聚合目标UniversalLib,如下图(你的Xcode有一点可能和下图不同,下图中的“IOS Device”,在你的Xcode可能显示为你真实的设备名称)。
点击Run按钮,编译聚合策略(aggregate scheme)里选择的目标。
为了看到结果,对项目导航栏里的Product文件夹下的libImageFilters.a右击,选择Show in Finder,
为了能看到文件夹的上下层次,切换Finder里显示文件夹的样式为列表样式,你将会看到一个新文件夹Debug-Universal(如果你在编译Release版本,文件夹名可能会是Release-Universal*),这个文件夹包含通用版本(Universal)静态库,如下图:
你会发现,除了模拟器和设备文件夹,通用静态库文件和头文件都出现了。(这行翻译可能有误,原文如下)。
You’ll find the usual headers and static library files are present, except this one links with both the simulator and the device.
以上,就是你为了创建自己的静态库而需要学习的东西。
简单来说,一个静态库项目和一个app项目很相似。你可以有一个或多个类,最终编译出来的产品是头文件个一个装有代码的.a文件,这个.a文件就是能被连接到多个app里的静态库。
在自己的App里使用静态库
在自己的App里使用ImageFilters类与直接从源码使用 差不多:导入头文件并且开始使用类。
(这行翻译可能不太准确,原文如下)
Using the ImageFilters class in your own app is not very different from using it directly from source: you import the header file and start using the class!
问题是,Xcode并不知道头文件或者二进制文件的位置。
把静态库放进项目里,这有两个方法:
方法1: 直接引用头文件和库的二进制文件(.a文件)。
方法2: 在你自己的项目里导入静态库项目,把静态库作为你项目的子项目。
选择其中一个方法或者其他方法,依赖于你自己的选择:
在你的项目中,是否需要静态库的源码和项目文件。
在这个教程里,这两个方法都在下面单独详细描述了。你可以尝试其中的一个方法,但是最好按照下面描述的顺序,把两个方法都了解一下。
在两个方法的开始部分,你将被要求下载一个zip文件,这个文件是教程Core Image Tutorial里的app的一个修改版本。(修改:使用了来自于静态库的新类ImageFilters)。
既然这个教程的目标是叫你怎样使用静态库,这个修改版本包含所有app需要的源码。
这样,你就能专注于项目使用静态库的配置。
方法1:引用头文件和库的二进制文件(.a文件)
对于这部分教程,你需要下载 starter project for this section。
拷贝下载好的zip文件到磁盘的任意一个文件夹下,解压zip。你将看到如下的文件夹结构:
为了让你方便,静态库头文件和.a文件的副本已经被包含在项目中,你就不用再次拷贝了,
但是项目并没有配置好来使用静态库,这就是你要做的
1
注意:经典Unix惯例,引用外部包,
2
引用的文件夹里面有一个包含头文件的文件夹“include”,
3
还有一个包含.a文件的文件夹 “lib”。
4
这个文件夹结构仅仅是一个惯例,不是强制性的。
5
在你的项目里引用静态库时,你并不需要遵循这个文件结构。在你自己的app里,你可以把头文件和库文件放在项目的任意位置,接下来,只要在你配置Xcode的时候,设置合理的目录路径就行。(这个目录路径必须能够让Xcode找到所需的.a文件和头文件)。
打开项目,编译、运行你的app,你将会看到如下编译错误提示:
如我所料,app无法找到头文件。
为了解决这个问题,你需要在项目里增加头文件搜索路径(Header Search Path),来指出头文件所在文件夹的位置。使用静态库的时候,总是首先配置头文件搜索路径(Header Search Path)
如下图所示,按照图示的1~4的步骤操作,在第3步点击了Build Settings标签后,在搜索栏输入关键字“header search”,以便快速定位到我们配置的那行参数。
双击Header Search Paths这行的后半空白部分,将会弹出下图界面,点击下图左下角的“+”按钮,然后输入一下信息:
$SOURCE_ROOT/include
$SOURCE_ROOT是Xcode的环境参数,指的是项目的根文件夹(the project’s root folder),
Xcode将会使用包含该项目的真实文件夹的路径地址来代替此参数,这样,即使你把整个项目移动到别的文件夹或者移动到别的电脑上,你也不用再更改路径参数了,因为Xcode利用此参数来帮你解决了这些麻烦的问题。
点击上图弹出框的外围部分来关闭弹出框,你将会看到,Xcode已经把参数“$SOURCE_ROOT”转换为项目所在文件夹的真实路径地址,如下图:
再次编译运行你的app,你会发现还会存在error,如下所示:
看起来情况不太好,但是Xcode也给出了部分提示信息。如果你仔细看看error信息,上面编译出现的“编译error信息”已经消失,取而代之,现在的error信息是连接错误(linker errors)。
这就说明,Xcode已经找到头文件并且用它来编译app,但是程序连接阶段,Xcode找不到ImageFliter类对象代码。
为什么呢?
这很简单,你还没告诉Xcode在哪里你呢个找到包含类实现代码的库文件(.a文件)。
(看样子,这也不是什么太糟糕的问题)
如下图所示,按照1~5的步骤操作,
1:点击项目文件
2:选择CoreImageFun目标
3:切换到Build Phases 标签
4:点击Link Binary With Libraries部分最左边的小箭头,展开该部分
5:点击该部分左下角的“+”按钮,弹出“添加库”界面
下图为“添加库”界面,点击左下角的Add Other…按钮,定位到该项目文件夹里的lib文件夹,找到libImageFilters.a库文件,添加进去。
当你做完以上步骤,下图就应该是你XcodeBuild Phases标签界面的样子(注意,libImageFilters.a已经被添加进去了。)
最后一步,在Xcode里添加-ObjC编译标识,
这个标识有时可以排除部分静态库代码,只把有效的代码导入项目。(见下段注解)
1
-ObjC编译标识: (此段为译者注)
2
例如,你的项目使用了第三方开源类JSONKit,而你的静态库也用到了这个JSONKit,那么你把静态库导入到项目后,这就产生冲突,所以你在把静态库导入项目时,静态库里的JSONKit.h头文件就不必导入,因为你的项目里已经有了JSONKit.h,JSONKit.m文件,使用**-ObjC**编译标识,Xcode就会变得聪明,Xcode知道你的项目里已经有了JSONKit的实现代码,即JSONKit.m文件,所以,静态库的JSONKit.m文件就不会被导入,这样,你的项目和静态库就共用你项目里面的JSONKit.m文件。你可以做一个相关实验尝试一下,把项目和静态库里的JSONKit.m文件里的内容写的不一样,看看,最终app里到底使用的哪个文件。
-ObjC编译标识功能很多,使用这个标识,可以让静态库里的类和类别(categories)文件被合理的加载进来。
你可以在Technical Q&A QA1490学习更多-ObjC编译标识相关知识。
添加-ObjC编译标识的方法如下图,点击Build Settings标签,在右上角的搜索框输入Other linker 即可搜索到。
在Other linker Flags(不用展开该行)这行的后半空白部分双击,弹出下图界面,点击弹出框左下角的“+”按钮,输入-ObjC即可
最后,编译、运行你的app;你不会再看到任何编译error,编译成功,app将会运行起来,如下图:
你可以尝试点击app上的按钮和滑动条,看看具体效果。
执行这些图片变化效果的代码并不在app,而是在静态库。
恭喜你!你刚刚在一个真实的app里编译、运行了你的第一个静态库。
包含头文件和静态库文件的方法,已经被用在很多知名的第三方库里,
例如AdMob, TestFlight或者那些不想提供源代码的商业库(百度地图、微博分享等等)。
方法2:将静态库作为子项目加载
对于这部分教程,你需要下载里一个文件,
点此下载
将下载好的zip文件拷贝你希望的位置,解压zip,你会看到如下图的文件结构
如果你看了上面的方法1,你会注意到,这次解压后的项目文件夹的根目录和方法1的不同:
这个项目文件夹根目录,没有任何头文件和.a文件(因为方法2不需要这些东西)。
取而代之,你将会看到,在本教程开始部分创建的ImageFilters静态库项目
作为子项目被添加到这个项目里。
在你做其它事情之前,你先编译、运行这个app,你将会被下图的error问候:
如果你看过本教程的方法1,你可能已经知道该如何修复这个error。
在你刚刚下载解压的项目里,你在ViewController类里使用了ImageFilters类
,但你仍然没有告诉Xcode头文件在哪里。你运行该项目时,Xcode找不到ImageFilters.h文件,所以编译失败。
为了把ImageFilters静态库项目导入进来作为子项目,有2个方法:
导入方法1:打开ImageFilters项目,你只需要从静态库项目窗口 拖曳 静态库项目的项目文件(一般为ImageFilters项目导航栏的最上面的那个文件) 到主项目(这里是CoreImageFun项目)窗口的导航栏中的任何地方即可,因为目前ImageFilters项目已经在窗口中打开,所以在主项目窗口里显示的ImageFilters文件只是单个文件,而不是树状结构。你要关闭这两个项目,然后再打开主项目CoreImageFun,你就会发现它已经变为树状结构。如下图
导入方法2:先关闭ImageFilters项目,然后在文件夹中,找到静态库的项目文件(即ImageFilters.xcodeproj文件),拖曳该文件到主项目CoreImageFun的导航栏中即可。如下图
注意:这两个方法的最终效果是一样的(如下图),在执行上面的方法2,尽量确保子项目(这里指的是ImageFilters静态库项目)并没有在Xcode中打开,
否则的话,你需要关闭这两个项目,再重新打开主项目。因为同一个项目,不能同时在两个窗口中打开。
现在Xcode知道了静态库子项目,你可以把添加静态库到项目依赖(Dependencies),如下图操作。
这就意味着,Xcode将确保在编译app前,静态库的代码是最新的(就是说,如果静态库项目有代码变更,主项目会自动重新编译静态库项目)。
Xcode添加项目依赖,如下图操作1~4,
点击上图步骤4的“+”号按钮会弹出另一个窗口,弹出的窗口如下图,在下拉列表里选择ImageFilters目标(而不是universalLib),点击右下角的“add”,完成。
完成上面的添加依赖的步骤后,Build Phases标签的Target Dependencies部分,将会增加一条,如下图:
最后一步,配置项目,将静态库连接到项目。如下图,展开Link Binary with libraries部分.
点击上图的“+”号按钮,弹出 下图的界面,
选择libImageFilters.a项目,点击add
添加完成后,Link Binary with Libraries部分如下图所示:
最后一步,添加-ObjC编译标识,添加步骤与上面的方法1的最后一步一样,如下图:
在上图Other linker Flags行的后半空白部分双击,弹出如下图,
点击弹出框左下角“+”按钮,添加 -ObjC。
编译运行你的app,app将会运行成功,如下图:
你可以操作一下刚刚运行成功的app。
与方法1一样,图片效果的逻辑代码,都在静态库中。
如果你按照方法1试验过添加静态库的过程(使用头文件和静态库),在处理方式上,和方法2有很多不同之处。
在方法2,你没有为Xcode配置header search paths参数。
另一个不同之处,你没有使用过通用静态库(Universal library)。
为什么会不同?
把静态库项目添加为子项目,Xcode就几乎为你解决了所有事情。
添加子项目和依赖之后,Xcode就知道了在哪里找到头文件和静态库文件,
而且静态库的架构会根据你app选择的参数来编译,这真是够方便的。
如果你使用自己的静态库,或者你需要访问源码和项目文件,
那么,导入静态库到项目较为方便的方法:方法2(即添加静态库项目作为子项目);
因为你集成子项目作为项目依赖,你需要操作、担心的事情就很少了,欧耶!
资源文件打包(这部分为译者添加)
在静态库里经常会遇到 图片、xib、各种外部文件等等,这些不能放在静态库里,
通常的做法是:把这些文件打成一个bundle包(扩展名为.bundle的文件)。
打bundle包,也可以建一个target,就像合并两种架构静态库的target的做法差不多。
具体做法点此
接下来去哪里?
你可以在这里下载这个教程的所有项目代码。
对于静态库的基本概念、怎样在自己的app使用静态库,希望这个教程会对你有所帮助。
接下来,用上面的知识去编译你自己的静态库!
你肯定有需要添加到所有项目里的通用类,把这些代码放在你自己的静态库里重用会是个很好的注意。
你还能够根据功能分类,创建多个静态库:
一个静态库放网络交互的代码,
一个放UI相关类的代码,等等。
这样你就能把你需要的模块添加到项目里。
为了进一步巩固、更深一步学习本教程的内容,
我也推荐你看看苹果官方文档里有关静态库的内容
Introduction to Using Static Libraries in iOS
希望你能喜欢这个教程,如果你有任何问题和想法,请加入论坛讨论。
以下为译者注
这篇文章使用Markdown语言编写,使用了如下工具:
Sublime Text 2编辑器
Sublime Text插件 Markdown Preview
Sublime Text插件 MarkdownEditing
本人英语也不是太好,翻译质量不是太高,如有不妥之处,欢迎在下面留言,指点批评。
原文:http://my.oschina.net/shede333/blog/164449
主页:http://my.oschina.net/shede333
版权声明:原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | [Creative Commons BY-NC-ND 3.0][]
本人英语也不是太好,翻译质量不是太高,如有不妥之处,欢迎指点批评。
点此查看文章 英文原文
创建IOS静态库
如果你开发ios有一段时间了,你可能有许多想在你大部分项目里重用的类和工具函数。
重用代码最容易的方法就是复制/黏贴,但是,这在代码维护上很快就变成一个噩梦。
既然每一个app都拥有一份 共享代码 的拷贝,这就很难保证所有 拷贝的代码 与 共享代码 在bug修正与更新的同步(一致性)。
这里就使用静态库来拯救噩梦。静态库就是类、函数、定义(definitions)和资源的一个包,使用静态库,你能把代码打包在一起,并且在你所有的项目间共享。
在这个教程,你将亲身经历使用两种不同的方法创建你自己的通用静态库。
你应该熟悉Objective-C and iOS开发,才能理解大致上这个教程。
如果你对怎样做一个相同的app 以及 图像滤光代码在库里的工作原理 感兴趣,Core Image的相关知识虽然不是必须的,但是对你会很有帮助。
准备开始高效的减少、重用和循环使用你的代码吧!
为什么使用静态库
你可能因为很多原因而创建静态库:
你想要把你或者你团队里的成员编写的类打包在一起,便于合理的使用,并且很容易与周围的人分享。
你想要让所有通用的代码集中到一起,便于你对代码的bug修复与更新。
你想和一些人分享你的代码库,但是你不想要他们看到你的代码。
随着开发时间的进展,你想要做一个版本库的快照。
在这个教程里,假设你已经看过Core Image Tutorial这个教程,并且理解了如何使用一些照片处理效果的代码。
你将会把那些代码添加到静态库里,并且在一个修改过的app里使用静态库。最终将会得到一个相同的app,但是会体现出面陈提到的所有优点。
开始 Let`s Go!
打开Xcode,选择File\New\Project,当Choose a template对话框出现时,选择iOS\Framework & Library\Cocoa Touch Static Library,如下图:
点击Next,在项目选项对话框,输入ImageFliters作为项目名称(project name),然后输入一个唯一的公司标识(company identifier ),并且勾选Use Automatic Reference Counting,而Include Unit Tests不要勾选。
点击Next,最后选择一个你想要保存项目的位置,然后点击Create。
Xcode已经创建了一个准备使用静态库的项目,并且项目里已经自动添加了ImageFliters类。(这难道不是Xcode的优点么?)这就是你将要编写的图像滤光代码的地方。
1
注意:你可以你想要的任何类到静态库里,甚至删除原来的代码(ImageFliters类)。
2
在这个教程,所有代码将会添加到Xcode开始自带的类ImageFliters。
既然你项目仍然空,让我们添加一些代码进去吧!
项目:Image Filters
这个库是为ios设计的,并且使用了UIKit框架,所以,你要做的第一件事是:在头文件,导入(import)UIKit框架,打开ImageFilters.h,并且在该文件最顶部添加下列代码:
1
#import <UIKit/UIKit.h>
接下来,在文件@interface ImageFilters : NSObject这一行的下面,黏贴如下声明代码:
1
@property (nonatomic,readonly) UIImage *originalImage;
2
3
- (id)initWithImage:(UIImage *)image;
4
- (UIImage *)grayScaleImage;
5
- (UIImage *)oldImageWithIntensity:(CGFloat)level;
这些头文件的声明代码,定义了类的公开接口。当其他开发者(包括你自己)使用这个库,通过读这个头文件,他们就知道类名和内嵌的库中的方法。
现在,是时候添加实现代码(implementation)了,
打开ImageFilters.m,在这行#import "ImageFilters.h"下面黏贴如下代码:
@interface ImageFilters()
@property (nonatomic,strong) CIContext context;
@property (nonatomic,strong) CIImage beginImage;
@end
上面的代码声明了许多用于类内部的属性,这些属性并不是公开的,所以任何使用这个库的app都不能访问这些属性。
最后,你需要实现类中的方法,在这行@implementation ImageFilters下面黏贴如下代码:
01
- (id)initWithImage:(UIImage *)image
02
{
03
self = [super init];
04
if (self) {
05
_originalImage = image;
06
_context = [CIContext contextWithOptions:nil];
07
_beginImage = [[CIImage alloc] initWithImage:_ originalImage];
08
}
09
return self;
10
}
11
12
- (UIImage*)imageWithCIImage:(CIImage *)ciImage
13
{
14
CGImageRef cgiImage = [self.context createCGImage:ciImage fromRect:ciImage.extent];
15
UIImage *image = [UIImage imageWithCGImage:cgiImage];
16
CGImageRelease(cgiImage);
17
18
return image;
19
}
20
21
- (UIImage *)grayScaleImage
22
{
23
if( !self.originalImage)
24
return nil;
25
26
CIImage *grayScaleFilter = [CIFilter filterWithName:@"CIColorControls" keysAndValues:kCIInputImageKey, self.beginImage, @"inputBrightness", [NSNumber numberWithFloat:0.0], @"inputContrast", [NSNumber numberWithFloat:1.1], @"inputSaturation", [NSNumber numberWithFloat:0.0], nil] outputImage;
27
28
CIImage *output = [CIFilter filterWithName:@"CIExposureAdjust" keysAndValues:kCIInputImageKey, grayScaleFilter, @"inputEV", NSNumber numberWithFloat:0.7], nil].outputImage;
29
30
UIImage *filteredImage = [self imageWithCIImage:output];
31
return filteredImage;
32
}
33
34
- (UIImage *)oldImageWithIntensity:(CGFloat)intensity
35
{
36
if( !self.originalImage )
37
return nil;
38
39
CIFilter *sepia = [CIFilter filterWithName:@"CISepiaTone"];
40
[sepia setValue:self.beginImage forKey:kCIInputImageKey];
41
[sepia setValue:@(intensity) forKey:@"inputIntensity"];
42
43
CIFilter *random = [CIFilter filterWithName:@"CIRandomGenerator" ;
44
45
CIFilter *lighten = [CIFilter filterWithName:@"CIColorControls"];
46
[lighten setValue:random.outputImage forKey:kCIInputImageKey];
47
[lighten setValue:@(1 - intensity) forKey:@"inputBrightness"];
48
[lighten setValue:@0.0 forKey:@"inputSaturation"];
49
50
CIImage *croppedImage = [lighten.outputImage imageByCroppingToRect:[self.beginImage extent]];
51
52
CIFilter *composite = [CIFilter filterWithName:@"CIHardLightBlendMode"];
53
[composite setValue:sepia.outputImage forKey:kCIInputImageKey];
54
[composite setValue:croppedImage forKey:kCIInputBackgroundImageKey];
55
56
CIFilter *vignette = [CIFilter filterWithName:@"CIVignette"];
57
[vignette setValue:composite.outputImage forKey:kCIInputImageKey ;
58
[vignette setValue:@(intensity * 2) forKey:@"inputIntensity"];
59
[vignette setValue:@(intensity * 30) forKey:@"inputRadius"];
60
61
UIImage *filteredImage = [self imageWithCIImage:vignette outputImage];
62
63
return filteredImage;
64
}
这上面代码实现了初始化和执行Core Image滤光效果的方法。
因为解释上面Core Image代码的功能,超出本教程的范围,
所以,您能在这个Core Image Tutorial教程里,学习Core Image和滤光的相关知识。
此时,你的静态库拥有ImageFilters类,这个类公开如下3个方法:
initWithImage :初始化类
grayScaleImage :创建图片的灰度效果
oldImageWithIntensity :创建图片的陈旧效果
现在编译并且运行你的库(将Run按钮旁边的编译目标改为iOS Device,这样便于后面找到.a文件),
你将会注意到,点击Xcode的“Run”按钮,只执行编译;
实际上,你不能通过运行你的静态库来看到结果,因为还没有app支持它。
静态库使用.a作为扩展名,而不是.app、.ipa等。
生成的静态库在Xcode导航栏的Products文件夹里,
右击 或者 “按住CTRL,单击” libImageFilters.a ,在弹出的菜单里选择Show in Finder,如下图:
Xcode将会在iFinder打开文件夹,你会看到如下:
一个共享库的最终最终产品有2个部分:
头文件(Header files):在include文件夹里,你找到静态库的所有公开头文件(.h文件)。
因为你只有1个公共类,所以这个文件夹只有单独的ImageFilters.h文件。
你在之后的app项目里面,为了让Xcode在编译时知道 输出类(the exported classes),需要用到这个头文件。
二进制库文件(Binary Library):Xcode生成的静态库文件是ImageFilters.a。当你想要在里使用这个库的时候,你需要使用这个文件来连接静态库。
导入新框架(framework)到app,与导入静态库很相似。
你只需要简单的导入框架头文件(framework header)和连接到app的框架代码(framework code)。
静态库使用小警告:默认情况下,编译生成的库文件仅仅适用于Xcode当前指定的架构(真机的框架为ARM,模拟器框架为i386)。
假如你目前的编译目标为模拟器,库文件将包含适用于i386架构的对象代码;
假如你目前的编译目标为真机设备,库文件将包含适用于ARM架构的对象代码;
你需要编译出两个版本的静态库文件,当你的编译目标在真机和模拟器间切换时,要连接使用对应的库文件。
那要怎么做才能解决上面的问题呢?
幸运的是,有一个很好的办法,
app项目不需要创建多个配置或者最终产品(build products),就能支持多个平台。
您可以创建一个universal binary,它包含这两个架构的对象代码。
Universal Binaries(通用二进制)
通用二进制 是一种包含多个架构对象代码的特殊二进制文件。
在Mac电脑的cpu从PowerPC (PPC) 过渡到 Intel (i386) 时,你对 通用二进制 可能会熟悉。
在过渡期间,Mac上的app通常装载了通用二进制,通用二进制在一个二进制文件内包含了两个平台的可执行代码,以便app在 Intel 和 PowerPC 架构的Mac电脑上均可运行。
支持ARM和i386架构的概念并没有太多区别。既然这样,静态库文件将包含适用于ios设备(ARM)和模拟器(i386)代码。Xcode将会识别通用二进制,并且你没戏编译app时,Xcode会根据当前的编译目标,选择适当的架构。
为了创建通用二进制库,你需要使用一个系统工具lipo。
不用担心小猫咪,lipo并不是指的上图中的脂肪(这句话属于美式幽默,我也不太懂)。
lipo 是一种允许你在通用二进制文件进行操作(操作包括:创建通用二进制文件、显示文件内容等等)的命令行工具。
在这个教程里,你将会使用lipo,将不同架构的二进制文件 合并成 包含多个架构的内容的单独二进制文件。
你能在命令行里直接使用lipo,但是在这个教程里面,你将会通过运行 一个命令行脚本来让Xcode为你工作,这个命令行能创建通用二进制文件。
在Xcode,一个聚合目标(An Aggregate Target)将会一次编译多个目标(target),包括命令行脚本。
在Xcode菜单栏里操作File/New/Target,出现如下对话框,选择 iOS/Other,再点击Aggregate,如下图
在 Product Name 输入UniversalLib,确保Project栏选中ImageFilters项目,如下图
在项目导航栏上点击ImageFilters(如下图操作1),然后选择UniversalLib目标(如下图操作2),切换到到Build Phases标签(如下图操作3),
这里就是你将创建行为的地方,当目标被编译时,这里的行为将会运行。
点击右下角Add Build Phase按钮,在弹出菜单里选择Add Run Script,如下图:
现在你要创建一个脚本。展开Run Script模块,将下面的shell代码黏贴进去。
01
# define output folder environment variable
02
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
03
04
# Step 1. Build Device and Simulator versions
05
xcodebuild -target ImageFilters ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ ROOT="${BUILD_ROOT}"
06
xcodebuild -target ImageFilters -configuration ${CONFIGURATION} -sdk iphonesimulator -arch i386 BUILD_DIR="${BUILD_DIR}" BUILD_ ROOT="${BUILD_ROOT}"
07
08
# make sure the output directory exists
09
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
10
11
# Step 2. Create universal binary file using lipo
12
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/lib${PROJECT_NAME}.a"
13
14
# Last touch. copy the header files. Just for convenience
15
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/include" "${UNIVERSAL_ OUTPUTFOLDER}/"
上面的代码并不复杂,下面将逐行解释代码如何运行:
UNIVERSAL_OUTPUTFOLDER :通用二进制文件“Debug-universal”将被拷贝到的目标文件夹的路径名称。
Step 1 代码的第二行,你将调用xcodebuild,并且指导它编译ARM架构二进制文件(你将会在这行看到参数-sdk iphoneos)。
下一行代码再次调用xcodebuild,并且在另一个文件夹为iPhone模拟器编译生成i386架构二进制文件,但这次的参数为-sdk iphonesimulator -arch i386。
(假如你对此很感兴趣,你您能在man page学习更多xcodebuild的相关知识)
Step 2 现在你已经拥有分别针对两种架构的两个.a静态库文件,你只要调用 lipo -create 并且设置它创建、输出一个通用二进制文件。
在最后一行,你将头文件拷贝到 用于生成通用二进制的文件夹内(使用cpshell命令行)。
你的“Run Script ”窗口应该如下图一样:
现在你要准备去编译通用版本的静态库。在策略选择下拉表,选择聚合目标UniversalLib,如下图(你的Xcode有一点可能和下图不同,下图中的“IOS Device”,在你的Xcode可能显示为你真实的设备名称)。
点击Run按钮,编译聚合策略(aggregate scheme)里选择的目标。
为了看到结果,对项目导航栏里的Product文件夹下的libImageFilters.a右击,选择Show in Finder,
为了能看到文件夹的上下层次,切换Finder里显示文件夹的样式为列表样式,你将会看到一个新文件夹Debug-Universal(如果你在编译Release版本,文件夹名可能会是Release-Universal*),这个文件夹包含通用版本(Universal)静态库,如下图:
你会发现,除了模拟器和设备文件夹,通用静态库文件和头文件都出现了。(这行翻译可能有误,原文如下)。
You’ll find the usual headers and static library files are present, except this one links with both the simulator and the device.
以上,就是你为了创建自己的静态库而需要学习的东西。
简单来说,一个静态库项目和一个app项目很相似。你可以有一个或多个类,最终编译出来的产品是头文件个一个装有代码的.a文件,这个.a文件就是能被连接到多个app里的静态库。
在自己的App里使用静态库
在自己的App里使用ImageFilters类与直接从源码使用 差不多:导入头文件并且开始使用类。
(这行翻译可能不太准确,原文如下)
Using the ImageFilters class in your own app is not very different from using it directly from source: you import the header file and start using the class!
问题是,Xcode并不知道头文件或者二进制文件的位置。
把静态库放进项目里,这有两个方法:
方法1: 直接引用头文件和库的二进制文件(.a文件)。
方法2: 在你自己的项目里导入静态库项目,把静态库作为你项目的子项目。
选择其中一个方法或者其他方法,依赖于你自己的选择:
在你的项目中,是否需要静态库的源码和项目文件。
在这个教程里,这两个方法都在下面单独详细描述了。你可以尝试其中的一个方法,但是最好按照下面描述的顺序,把两个方法都了解一下。
在两个方法的开始部分,你将被要求下载一个zip文件,这个文件是教程Core Image Tutorial里的app的一个修改版本。(修改:使用了来自于静态库的新类ImageFilters)。
既然这个教程的目标是叫你怎样使用静态库,这个修改版本包含所有app需要的源码。
这样,你就能专注于项目使用静态库的配置。
方法1:引用头文件和库的二进制文件(.a文件)
对于这部分教程,你需要下载 starter project for this section。
拷贝下载好的zip文件到磁盘的任意一个文件夹下,解压zip。你将看到如下的文件夹结构:
为了让你方便,静态库头文件和.a文件的副本已经被包含在项目中,你就不用再次拷贝了,
但是项目并没有配置好来使用静态库,这就是你要做的
1
注意:经典Unix惯例,引用外部包,
2
引用的文件夹里面有一个包含头文件的文件夹“include”,
3
还有一个包含.a文件的文件夹 “lib”。
4
这个文件夹结构仅仅是一个惯例,不是强制性的。
5
在你的项目里引用静态库时,你并不需要遵循这个文件结构。在你自己的app里,你可以把头文件和库文件放在项目的任意位置,接下来,只要在你配置Xcode的时候,设置合理的目录路径就行。(这个目录路径必须能够让Xcode找到所需的.a文件和头文件)。
打开项目,编译、运行你的app,你将会看到如下编译错误提示:
如我所料,app无法找到头文件。
为了解决这个问题,你需要在项目里增加头文件搜索路径(Header Search Path),来指出头文件所在文件夹的位置。使用静态库的时候,总是首先配置头文件搜索路径(Header Search Path)
如下图所示,按照图示的1~4的步骤操作,在第3步点击了Build Settings标签后,在搜索栏输入关键字“header search”,以便快速定位到我们配置的那行参数。
双击Header Search Paths这行的后半空白部分,将会弹出下图界面,点击下图左下角的“+”按钮,然后输入一下信息:
$SOURCE_ROOT/include
$SOURCE_ROOT是Xcode的环境参数,指的是项目的根文件夹(the project’s root folder),
Xcode将会使用包含该项目的真实文件夹的路径地址来代替此参数,这样,即使你把整个项目移动到别的文件夹或者移动到别的电脑上,你也不用再更改路径参数了,因为Xcode利用此参数来帮你解决了这些麻烦的问题。
点击上图弹出框的外围部分来关闭弹出框,你将会看到,Xcode已经把参数“$SOURCE_ROOT”转换为项目所在文件夹的真实路径地址,如下图:
再次编译运行你的app,你会发现还会存在error,如下所示:
看起来情况不太好,但是Xcode也给出了部分提示信息。如果你仔细看看error信息,上面编译出现的“编译error信息”已经消失,取而代之,现在的error信息是连接错误(linker errors)。
这就说明,Xcode已经找到头文件并且用它来编译app,但是程序连接阶段,Xcode找不到ImageFliter类对象代码。
为什么呢?
这很简单,你还没告诉Xcode在哪里你呢个找到包含类实现代码的库文件(.a文件)。
(看样子,这也不是什么太糟糕的问题)
如下图所示,按照1~5的步骤操作,
1:点击项目文件
2:选择CoreImageFun目标
3:切换到Build Phases 标签
4:点击Link Binary With Libraries部分最左边的小箭头,展开该部分
5:点击该部分左下角的“+”按钮,弹出“添加库”界面
下图为“添加库”界面,点击左下角的Add Other…按钮,定位到该项目文件夹里的lib文件夹,找到libImageFilters.a库文件,添加进去。
当你做完以上步骤,下图就应该是你XcodeBuild Phases标签界面的样子(注意,libImageFilters.a已经被添加进去了。)
最后一步,在Xcode里添加-ObjC编译标识,
这个标识有时可以排除部分静态库代码,只把有效的代码导入项目。(见下段注解)
1
-ObjC编译标识: (此段为译者注)
2
例如,你的项目使用了第三方开源类JSONKit,而你的静态库也用到了这个JSONKit,那么你把静态库导入到项目后,这就产生冲突,所以你在把静态库导入项目时,静态库里的JSONKit.h头文件就不必导入,因为你的项目里已经有了JSONKit.h,JSONKit.m文件,使用**-ObjC**编译标识,Xcode就会变得聪明,Xcode知道你的项目里已经有了JSONKit的实现代码,即JSONKit.m文件,所以,静态库的JSONKit.m文件就不会被导入,这样,你的项目和静态库就共用你项目里面的JSONKit.m文件。你可以做一个相关实验尝试一下,把项目和静态库里的JSONKit.m文件里的内容写的不一样,看看,最终app里到底使用的哪个文件。
-ObjC编译标识功能很多,使用这个标识,可以让静态库里的类和类别(categories)文件被合理的加载进来。
你可以在Technical Q&A QA1490学习更多-ObjC编译标识相关知识。
添加-ObjC编译标识的方法如下图,点击Build Settings标签,在右上角的搜索框输入Other linker 即可搜索到。
在Other linker Flags(不用展开该行)这行的后半空白部分双击,弹出下图界面,点击弹出框左下角的“+”按钮,输入-ObjC即可
最后,编译、运行你的app;你不会再看到任何编译error,编译成功,app将会运行起来,如下图:
你可以尝试点击app上的按钮和滑动条,看看具体效果。
执行这些图片变化效果的代码并不在app,而是在静态库。
恭喜你!你刚刚在一个真实的app里编译、运行了你的第一个静态库。
包含头文件和静态库文件的方法,已经被用在很多知名的第三方库里,
例如AdMob, TestFlight或者那些不想提供源代码的商业库(百度地图、微博分享等等)。
方法2:将静态库作为子项目加载
对于这部分教程,你需要下载里一个文件,
点此下载
将下载好的zip文件拷贝你希望的位置,解压zip,你会看到如下图的文件结构
如果你看了上面的方法1,你会注意到,这次解压后的项目文件夹的根目录和方法1的不同:
这个项目文件夹根目录,没有任何头文件和.a文件(因为方法2不需要这些东西)。
取而代之,你将会看到,在本教程开始部分创建的ImageFilters静态库项目
作为子项目被添加到这个项目里。
在你做其它事情之前,你先编译、运行这个app,你将会被下图的error问候:
如果你看过本教程的方法1,你可能已经知道该如何修复这个error。
在你刚刚下载解压的项目里,你在ViewController类里使用了ImageFilters类
,但你仍然没有告诉Xcode头文件在哪里。你运行该项目时,Xcode找不到ImageFilters.h文件,所以编译失败。
为了把ImageFilters静态库项目导入进来作为子项目,有2个方法:
导入方法1:打开ImageFilters项目,你只需要从静态库项目窗口 拖曳 静态库项目的项目文件(一般为ImageFilters项目导航栏的最上面的那个文件) 到主项目(这里是CoreImageFun项目)窗口的导航栏中的任何地方即可,因为目前ImageFilters项目已经在窗口中打开,所以在主项目窗口里显示的ImageFilters文件只是单个文件,而不是树状结构。你要关闭这两个项目,然后再打开主项目CoreImageFun,你就会发现它已经变为树状结构。如下图
导入方法2:先关闭ImageFilters项目,然后在文件夹中,找到静态库的项目文件(即ImageFilters.xcodeproj文件),拖曳该文件到主项目CoreImageFun的导航栏中即可。如下图
注意:这两个方法的最终效果是一样的(如下图),在执行上面的方法2,尽量确保子项目(这里指的是ImageFilters静态库项目)并没有在Xcode中打开,
否则的话,你需要关闭这两个项目,再重新打开主项目。因为同一个项目,不能同时在两个窗口中打开。
现在Xcode知道了静态库子项目,你可以把添加静态库到项目依赖(Dependencies),如下图操作。
这就意味着,Xcode将确保在编译app前,静态库的代码是最新的(就是说,如果静态库项目有代码变更,主项目会自动重新编译静态库项目)。
Xcode添加项目依赖,如下图操作1~4,
点击上图步骤4的“+”号按钮会弹出另一个窗口,弹出的窗口如下图,在下拉列表里选择ImageFilters目标(而不是universalLib),点击右下角的“add”,完成。
完成上面的添加依赖的步骤后,Build Phases标签的Target Dependencies部分,将会增加一条,如下图:
最后一步,配置项目,将静态库连接到项目。如下图,展开Link Binary with libraries部分.
点击上图的“+”号按钮,弹出 下图的界面,
选择libImageFilters.a项目,点击add
添加完成后,Link Binary with Libraries部分如下图所示:
最后一步,添加-ObjC编译标识,添加步骤与上面的方法1的最后一步一样,如下图:
在上图Other linker Flags行的后半空白部分双击,弹出如下图,
点击弹出框左下角“+”按钮,添加 -ObjC。
编译运行你的app,app将会运行成功,如下图:
你可以操作一下刚刚运行成功的app。
与方法1一样,图片效果的逻辑代码,都在静态库中。
如果你按照方法1试验过添加静态库的过程(使用头文件和静态库),在处理方式上,和方法2有很多不同之处。
在方法2,你没有为Xcode配置header search paths参数。
另一个不同之处,你没有使用过通用静态库(Universal library)。
为什么会不同?
把静态库项目添加为子项目,Xcode就几乎为你解决了所有事情。
添加子项目和依赖之后,Xcode就知道了在哪里找到头文件和静态库文件,
而且静态库的架构会根据你app选择的参数来编译,这真是够方便的。
如果你使用自己的静态库,或者你需要访问源码和项目文件,
那么,导入静态库到项目较为方便的方法:方法2(即添加静态库项目作为子项目);
因为你集成子项目作为项目依赖,你需要操作、担心的事情就很少了,欧耶!
资源文件打包(这部分为译者添加)
在静态库里经常会遇到 图片、xib、各种外部文件等等,这些不能放在静态库里,
通常的做法是:把这些文件打成一个bundle包(扩展名为.bundle的文件)。
打bundle包,也可以建一个target,就像合并两种架构静态库的target的做法差不多。
具体做法点此
接下来去哪里?
你可以在这里下载这个教程的所有项目代码。
对于静态库的基本概念、怎样在自己的app使用静态库,希望这个教程会对你有所帮助。
接下来,用上面的知识去编译你自己的静态库!
你肯定有需要添加到所有项目里的通用类,把这些代码放在你自己的静态库里重用会是个很好的注意。
你还能够根据功能分类,创建多个静态库:
一个静态库放网络交互的代码,
一个放UI相关类的代码,等等。
这样你就能把你需要的模块添加到项目里。
为了进一步巩固、更深一步学习本教程的内容,
我也推荐你看看苹果官方文档里有关静态库的内容
Introduction to Using Static Libraries in iOS
希望你能喜欢这个教程,如果你有任何问题和想法,请加入论坛讨论。
以下为译者注
这篇文章使用Markdown语言编写,使用了如下工具:
Sublime Text 2编辑器
Sublime Text插件 Markdown Preview
Sublime Text插件 MarkdownEditing
本人英语也不是太好,翻译质量不是太高,如有不妥之处,欢迎在下面留言,指点批评。
原文:http://my.oschina.net/shede333/blog/164449
发表评论
-
SOCK_STREAM和SOCK_DGRAM
2015-07-23 20:08 1640sock_stream 是有保障的(即能保证数据正确传送到 ... -
SOCKET bind INADDR_LOOPBACK和INADDR_ANY的区别
2015-07-23 19:49 2057今天写程序时候,服务器端启动了,然后客户端总是连接不上,con ... -
htons()
2015-07-23 19:26 580在C/C++写网络程序的时候,往往会遇到字节的网络顺序和主机顺 ... -
使用symbolicatecrash分析crash文件
2015-03-10 11:32 1179原文 http://www.cnblogs.com/ning ... -
程序设计中的计算复用(Computational Reuse)
2015-02-10 10:18 662从斐波那契数列说起 ... -
didReceiveMemoryWarning
2015-02-09 16:11 541IPhone下每个app可用的内存是被限制的,如果一个app使 ... -
iOS开发中怎么响应内存警告
2015-02-09 16:08 654好的应用应该在系统内存警告情况下释放一些可以重新创建的资源。在 ... -
ASIHTTPRequest多次重复请求的问题
2014-12-17 14:34 641在一个车票订购的项目中,点击一次订购,却生成了2次订单,通过抓 ... -
从 CloudKit 看 BaaS 服务的趋势
2014-09-26 11:51 726从 6 月份 WWDC 苹果发布 ... -
ios编程--AVCapture编程理解
2014-09-26 11:03 9230、媒体采集的几个东西。这里所需要明白的是,在这个流程中,这里 ... -
NSURLProtocol
2014-09-25 10:42 8191、http://nshipster.com/nsurlpro ... -
关于iOS8的extension插件
2014-09-25 10:41 1279关于iOS8的extension插件,有兴趣的同学可以参考一下 ... -
【转】ios app在itunesConnect里面的几种状态
2014-08-05 10:34 1145Waiting for Upload (Yellow) Ap ... -
[转]iOS Dev (45) iOS图标与切片处理工具Prepo
2014-02-07 17:02 1033iOS Dev (45) iOS图标与切片处理工具Prepo ... -
phoneGap开发IOS,JS调用IOS方法/phoneGap插件开发
2014-01-13 17:49 1244前沿 废话不说phoneGap是什么不多介绍,官方网站: h ... -
如何在IOS平台下搭建PhoneGap开发环境(PhoneGap2.5)
2014-01-13 15:23 747由于在下最近在做基于HTML5的跨平台移植,搭建环境的时候着实 ... -
xcode 4 制作静态库详解
2013-12-20 18:27 533最近在做Apple的IOS开发,有开发静态库的需求,本身IOS ... -
封装自己的控件库:iPhone静态库的应用
2013-12-20 17:03 581由于iPhone 控件的极度匮乏和自定义组件在重用上的限制,在 ... -
iphone:使用NSFileManager取得目录下所有文件(遍历所有文件)
2013-11-18 17:56 868From:http://note.sdo.com/u/xiao ... -
ios7-录音权限访问-AVAudioSession
2013-11-15 19:39 11805///新增api,获取录音权限. 返回值,YES为无拒绝,NO ...
相关推荐
**iOS-lame静态库** 在iOS开发中,音频处理是一个重要的环节,特别是在音乐播放、语音聊天等应用中。`lame`是一个著名的免费且开源的MP3编码库,它为开发者提供了将音频数据编码成高质量MP3格式的能力。本文将详细...
本资源"ios-静态库实现版本对比.zip"可能包含了不同版本的静态库实现,以及一个用于比较这些版本的工具"VersionTool"。 静态库的优点在于它可以被直接链接到应用程序中,形成一个完整的二进制可执行文件,避免了...
总的来说,创建和使用iOS的Xcode静态库是一项实用的技能,它可以提高代码组织的合理性,简化项目结构,同时也便于代码的维护和分发。通过理解和掌握这一技术,开发者能更好地管理自己的代码资源,提高开发效率。
《iOS CMake构建系统详解——以ios-cmake-master项目为例》 在iOS开发中,CMake作为一款跨平台的构建工具,已经逐渐被广大开发者所接纳。尤其在涉及到跨平台项目或者需要进行复杂构建配置时,CMake的优势就显现出来...
在iOS开发中,创建一个Framework静态库是一种常见的方式,它可以帮助开发者封装常用代码,提高代码重用性,并保护知识产权。本篇文章将详细讲解如何通过一个简单的demo来创建一个iOS的静态库。 首先,理解框架...
资源名称:iOS开发视频教程资源目录:【】iOS开发视频教程-第01讲-iOS历史介绍【】iOS开发视频教程-第02讲-XCode安装【】iOS开发视频教程-第03讲-UIView_PPT【】iOS开发视频教程-第04讲-UILabel【】iOS开发视频教程-...
本篇将深入探讨如何创建并测试iOS静态库。 首先,我们来理解静态库的基本概念。静态库在编译时会被直接链接到目标应用中,成为应用的一部分。这意味着应用的体积可能会增加,因为它包含了库的所有代码,但同时也...
iOS-nRF-Mesh-Library是Nordic公司为iOS平台开发的一个开源库,旨在简化蓝牙Mesh技术的集成和开发。这个库提供了用于控制和通信的API,帮助开发者在iPhone或iPad上实现对蓝牙Mesh网络的管理,如配置节点、发布和接收...
本文将详细介绍如何创建和使用iOS静态库,并提供源码示例。 一、静态库的创建 1. 创建Xcode工程:首先,我们需要在Xcode中创建一个新的项目,选择"File" -> "New" -> "Project",然后在模板选择器中选择"Cocoa ...
本篇将详细介绍如何创建并使用iOS静态库,以及`MyStaticLib`和`MyStaticLibUse`这两个项目在其中的作用。 一、iOS静态库的创建 1. **创建静态库工程** - 打开Xcode,选择"File" -> "New" -> "Project",在模板...
本资源提供的是针对iOS平台,兼容arm64和x86_64架构的通用静态库——libssl.a和libcrypto.a。 1. **OpenSSL库介绍** OpenSSL是一个强大的安全套接层(SSL)和传输层安全(TLS)协议实现,同时也包含了各种常用的...
思科 cisco 镜像 ios c2600-adventerprisek9-mz.124-18.bin 文件由多位CCIE水平人员测试使用后拷贝,dy下可用
很抱歉,但根据提供的文件信息,"IOS8-静态.ppt" 的内容似乎是一个PowerPoint演示文稿的模板,包含了各种占位符文本和日期,以及一个名为"亮亮图文旗舰店"的店铺链接,这通常用于填充实际内容的商业演示。...
1. **创建静态库项目**:在Xcode中选择“File” > “New” > “Project”,然后选择“iOS” > “Framework & Library” > “Cocoa Touch Static Library”。给项目起个名字,比如"LibraryStudy",并选择合适的设备...
"FFmpeg-iOS-build-script-master.zip" 提供的正是一个专为iOS平台定制的FFmpeg编译脚本。 这个压缩包中的核心是“FFmpeg-iOS-build-script-master”文件夹,里面包含了编译FFmpeg到iOS平台所需的全部配置和脚本。...
这个压缩包提供了预编译的iOS版OpenSSL静态库,方便开发者直接集成到他们的项目中,无需从源码编译,节省了时间和资源。OpenSSL库包含了各种加密算法,如RSA、AES、DES等,以及SSL/TLS协议实现,对于需要处理HTTPS...
在您提供的压缩包中包含的全平台静态库意味着您将能够为 Android、iOS、Windows、Mac 和 Linux 平台的项目集成 `curl` 功能。 1. **curl 的主要功能**: - 支持多种网络协议:HTTP、HTTPS、FTP、FTPS、TFTP、SMTP...
绝对的干货分享!使用率极高的iOS 11元件库点击就可以...呕心沥血整理的55个iOS 11使用率极高的元件库,提高你的工作效率! ·高还原度的系统插件! ·大部分元素颜色文字可手动修改! ·即拉即用,无需二次修改!
9. **Box2D**:cocos2d-iOS支持集成Box2D物理引擎,这是一个流行的2D物理模拟库,可以创建真实世界的物理行为,如碰撞检测和重力效果。 10. **Chipmunk**:另一种可选的物理引擎,相比Box2D更轻量级,同样可以实现...