iOS静态库的那些坑

来源:互联网 发布:孙娜恩的脸肿了知乎 编辑:程序博客网 时间:2024/05/18 05:40

http://www.51each.com/blogs/6686.htm

最近项目需要我开发一个内嵌iOS的SDK,直白说就是要写一个通用的静态库。很久以前我也做过类似的事,不过由于不是商用的东西,没有深入研究,但这次不一样了。原以为很简单的东西,最后我还是踩了不少的坑,所以特此纪录一下。 

一、基本背景网上有很多介绍静态库的资料,基本的步骤我就不重复叙述,这里的介绍很详细:http://www.cocoachina.com/applenews/devnews/2013/1204/7468.html 我只重复几个关键点:

1、静态库所依赖的dylib或者framework,最终使用静态库的程序也需要引用。

2、静态库是二进制代码,区分处理器类型的,可以使用lipo -create –output创建支持多处理器的静态库。(详情可以参照我之前博客转载的命令列表http://www.cnblogs.com/ipinka/p/3239910.html)

3、编译时请把Build Configuration选择为Release

4、Objective-C无真正私有方法,所以你静态库想要暴露多少接口,多少类,完全取决于你的头文件。

 二、命名问题iOS程序员大多都会使用第三方的开源代码,对于直接在应用中使用这些代码,一般是不会对其做出什么修改的。但是静态库不一样,在静态库中使用开源代码必须要注意几件事。 

1、类的命名:由于Objective-C没有命名空间,所以定义名字相同的类会视为重复定义。因此使用开源代码的时候,必须要对其类名进行修改。我建议是加上三位字母以上的前缀,否则还是有可能重复的。

2、Category:作为Objective-C的一个重要特性之一,开源代码也用了不少category。当然,category添加的方法即使名字重复了也不会报错,但是会存在其中一个方法覆盖了另一个。由于开源代码也存在版本不同的问题,因此为了避免造成混乱,建议还是给category添加的方法添加前缀。Category除了可以添加方法,还可以重写方法,但对于在静态库中使用category重写和method swizzling改变行为的代码,我建议还是敬而远之。这对静态库使用者会造成很多迷惑,特别一旦出现与预期不符的情况时,基本上是难以找出原因。当然,如果是有非常特殊需求的时候,这些要求也是可以放松的,不过必须要在嵌入文档中说明。还有一点需要说明的是,使用category重写的类load方法,是允许的。因为,无论用多少个category,加了多少个load方法,这些方法都会一一被执行。至于执行顺序是不保证的,详情可以自行google。

3、全局变量和函数:全局变量重名是必然编译失败的,这是C时代的产物,因此也是需要添加前缀来避免重名。使用static修饰的文件作用域变量可以不用修改,(请了解好C的作用域基础,以及extern、static),C函数也是同样道理。 

三、调试log

1、预处理:很多开源库都会在debug时打开log,release关闭log。发布到store的应用不应该把调试的log都打印出来。因此在打包静态库时,把Build Configuration选择为Release一般可以把多数开源库的log都去掉了。原理是利用预处理检测"DEBUG"标记来去掉log代码。但是如果静态库使用者希望能在开发时看到log,那就只能分开打包debug和release的包了。

2、运行时:还有一种方法是在运行时判断是否附加了调试器,苹果官方提供了一个方法检测https://developer.apple.com/library/mac/qa/qa1361,这样就可以打出一个只在调试时才会打印log的包。但是,即使如此我也建议不宜多用,因为这需要运行时判断debug状态,也算是有额外的开销(当然可以用静态变量纪录,减少重复调用函数的开销)。而且,调试内容写入发布的应用,反编译也是可以导出相关信息。所以,需要斟酌使用。

3、宏:对于那些习惯使用__func__,__FILE__,__LINE__等宏的程序员还是注意一下,这些宏编译后是会变成字符串或者数字的,因此暴露了原文件的信息。所以,发布的包建议去掉这些信息。

 四、静态库冲突

上面说了那么多,都是为了写出一个规范的静态库。不过很可惜,你只能控制你力所能及的部分,对于别人的代码你很难去干涉。例如,你极有可能碰上了两个静态库发生冲突的情况。一般情况下,你可以建议其中一个静态库的所有者改进他的代码,但是也有可能这些库都没人维护或者他们拒绝修改。但项目开发必须要用到,遇到这种情况只能把其中一个冲突的静态库做出改造了。 

1、利用lipo –info查看静态库包含的包,然后全部分离出来。其中有个小问题就是对于armv7s等处理器,lipo识别不好,所以需要改为调用"xcrun -sdk iphoneos lipo"。

2、利用ar解包其中一个静态库,然后把发生冲突的.o文件删除,然后用libtool重新打包。

3、最后对每种处理器重复步骤2,然后用lipo重新合并静态库,最后替换原来的静态库。 (以上命令可以参照http://www.cnblogs.com/ipinka/p/3239910.html) 不过这么做有个不稳定的因素的是,那个被替换掉的.o有可能版本和现有的不同,又有可能被添加了自有方法等。对于这类情况,我也无能为力了,遇上这么坑人的静态库,只能束手无策。 

五、静态库快速改成frameworkXcode没有自带打包framework的工程模板,当然网上有很多方法可以添加framework的模板。但对于现有的静态库,其实也可以快速改造成framework。首先需要简述一个概念,对于iOS的应用,第三方的framework和系统提供的framework是有区别的。第三方的framework实际上是静态嵌入到应用,也就是和静态库无异。而系统的framework是动态库,也就是运行时才加载的,不会嵌入到应用。AppStore理论上是不会让使用第三方动态库的应用通过审核的。因此我们使用的第三方framework其实本质上就是静态库,所以才使这个技巧能成功应用。

1、首先新建一个"XXXXX.framework"的文件夹

2、把.a文件拖进去并重命名为"XXXXX"(不带扩展名)

3、在framework文件夹内新建文件夹"Headers",把头文件拖进去。就这样,一个"伪"framework就完成了,其实和.a文件相比只是文件组织形式上好看了,没很大实际意义。  

关于静态库的相关知识就介绍完毕了,读者有任何疑问可以留言交流,文章可转载,保留署名Pinka-cnblogs。

0 0