iOS-让静态库中的category变得可用

如果在静态库中定义了一个category的话,APP中直接使用的话,会出现"undefined symbols"的错误,明明程序中已经定义了啊,为什么呢?

当你组建(build)一个动态库(.dylib)、一个framework、一个可加载的bundle(bundle)或者一个可执行的二进制文件的时候,这些文件被链接器链接到一起来生成一些操作系统认为“可用的”的东西,例如一些可以直接载入指定内存地址的东西。

然而,当你组建静态库(static library)的时候,所有的对象文件被简单的添加到一个大的归档文件,这就是.a(archive)文件的由来。所以.a文件就是一些对象文件的归档(想象一下没有经过压缩的tar归档或者zip归档)。拷贝单个.a文件要比拷贝一堆.o文件简单的多(java也是一样,为了使用方便你可以把一些.class文件放到一个.jar归档里)。

在把二进制链接到静态库(=archive)的时候,编译器会获得一张含有在归档里所有符号的表然后检查哪些符号被这些二进制文件引用了。只有包含被引用的符号的对象文件会被链接器真正的载入,并被链接进程处理。例如,如果你的文档中有50个对象文件,但是只有20个包含被二进制使用的符号,那么只有这20个文件会被链接器载入,另外30个会被链接进程完全忽略。

在C和C++代码里,这种机制能很好地工作,因为这些语言尽可能在编译时(C++也有一些runtime特性)去做这些事。然而Objective-C是一种不同的语言。OC非常依赖runtime特性,而且很多OC的特性都是只支持runtime的。OC类里面实际上也有类似于C函数或全局C变量的符号表(至少现在的OC runtime是这样)。编译器可以识别出一个类有没有被引用,从而确定这个类是不是被使用了。如果你使用了静态库里的对象文件中的类,这个对象文件就会被链接器载入,因为链接器发现它的一个符号被使用了。

类别是runtime下特有的特性,类别并不会像类或者函数一样被符号化,也就是说,编译器并不能检测到类别是不是被使用了。

如果链接器载入了一个包含OC代码的对象文件,这些OC代码的所有部分都是编译阶段的一部分。所以当一个包含类别的对象文件因为任何符号被认为“在使用”(可能是一个类,可能是一个函数,也可能是一个全局变量),它的类别也会被载入并在runtime中变得可用。一个只包含类别的对象文件里没有被编译器认为“在使用”的符号,所以是不会被载入的。

解决方法
为了使在静态库中的类别能被使用,可以通过下面的5种方法解决:

1.    通过在Other Linker Flags添加-all_load,它会告诉编译器“对于所有文档中的所有对象文件,不管里面的符号有没有被用到,都载入”,这种方法确实可以,但是会产生比较大的二进制文件。

2.    另一种方法是添加-force_load和指定的路径,这种方法和-all_load很像,不同的是它只使用指定的归档。

3.    最受欢迎的方法是在 Other Linker Flags 中添加"-ObjC",这个标识告诉编译器“如果你在文档里的对象文件中发现了OC代码,就把它载入“,Category里当然也有OC代码。使用这种方法不会载入任何没有OC代码的文件

4.    另一种解决方法是新Xcode里build setting中的"Perform Single-Object Prelink",如果启用这个选项,所有的对象文件都会被合并成一个单文件(这不是真正的链接,所以叫做预链接),这个对象文件(有时被称做主对象文件(masterobject file))被添加到文档中。现在如果主对象文件中的任何符号被认为是“在使用”,整个主对象文件都会被认为在使用,这样它里面的OC部分就会被载入了。因为里面的类都被正常符号化了,所以能使从这样的静态库中使用所有的category。

5.    最后一种解决方法是在只有category的源文件里添加Fakesymbol。如果你想在runtime里使用category,一定要确保你以某种方法在编译时引用了fake symbol,这会使得对象文件以及它里面的OC代码被载入。例如,它可以是一个有空函数体的函数,也可以是一个被访问的全局变量(例如一个全局的int变量,只要它被读或者写了一次就足够了)。和上面其他的解决方法不一样,这种解决方法可以控制哪些category可以在runtime里被编译后的代码使用(可以通过使用这个符号,使它们被链接并变得可用;也可以不使用这个符号,这样链接器就会忽略它)。

如果你有一个包含上百个对象的静态库,但是你的二进制文件只使用了其中的几个,你应该避免使用1~4的方法。否则你会获得一个非常大的包含了所有类(可能这些类根本没被用到)的二进制文件。对于一个类别,你通常不用做特殊的处理;而对于一个类别,考虑一下第5中解决方案,它可以让你只包含你想要的类别。

例如:如果你想使用NSData的类别,添加两个方法compressionData和decompressionData,你可以创建一个这样的头文件。

// NSData+Compress.h  
@interface NSData (Compression)  
    - (NSData *)compressedData;  
    - (NSData *)decompressedData;  
@end  

// NSData+Compress  
@implementation NSData (Compression)  
    - (NSData *)compressedData   
    {  
        // ... magic ...  
    }  
   
    - (NSData *)decompressedData  
    {  
        // ... magic ...  
    }  
@end  
   
void import_NSData_Compression ( ) { } //注意这里***************  

然后确保在你的代码里调用了import_NSData_Compression这个方法。是不是真的调用或者调用的次数并不重要,实际上你并不需要真的去调用它,只需要让编译器认为你调用了这个方法就行了。你可以吧这段代码添加到你的工程的任何地方

__attribute__((used)) static void importCategories ()  
{  
    import_NSData_Compression();  
    // add more import calls here  
}  

你并不需要在你的代码里调用importCategories (),attribute将会让编译器认为它被调用了。

英文原文链接
中文翻译链接

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容