iOS中静态库(static library, .a文件)中的category变得可用

2024-01-07 05:08

本文主要是介绍iOS中静态库(static library, .a文件)中的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 ...}
@endvoid import_NSData_Compression ( ) { } //注意这里***************

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

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

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


参考:

1. 怎样让动态库(static library)中的category变得可用

这篇关于iOS中静态库(static library, .a文件)中的category变得可用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/578813

相关文章

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

native和static native区别

本文基于Hello JNI  如有疑惑,请看之前几篇文章。 native 与 static native java中 public native String helloJni();public native static String helloJniStatic();1212 JNI中 JNIEXPORT jstring JNICALL Java_com_test_g

Thymeleaf:生成静态文件及异常处理java.lang.NoClassDefFoundError: ognl/PropertyAccessor

我们需要引入包: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>sp

【iOS】MVC模式

MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序

Sentinel 高可用流量管理框架

Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。 Sentinel 具有以下特性: 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应

JAVA用最简单的方法来构建一个高可用的服务端,提升系统可用性

一、什么是提升系统的高可用性 JAVA服务端,顾名思义就是23体验网为用户提供服务的。停工时间,就是不能向用户提供服务的时间。高可用,就是系统具有高度可用性,尽量减少停工时间。如何用最简单的方法来搭建一个高效率可用的服务端JAVA呢? 停工的原因一般有: 服务器故障。例如服务器宕机,服务器网络出现问题,机房或者机架出现问题等;访问量急剧上升,导致服务器压力过大导致访问量急剧上升的原因;时间和

VMware8实现高可用(HA)集群

陈科肇 =========== 操作系统:中标麒麟高级操作系统V6 x86-64 实现软件:中标麒麟高可用集群软件 ======================== 1.环境的规划与配置 硬件要求 服务器服务器至少需要 2 台,每台服务器至少需要 2 块网卡以做心跳与连接公网使用存储环境 建议使用一台 SAN/NAS/ISCSI 存储作为数据共享存储空间 软

用Cri-O,Sealos CLI,Kubeadm方式部署K8s高可用集群

3.6 Cri-O方式部署K8s集群 注意:基于Kubernetes基础环境 3.6.1 所有节点安装配置cri-o [root@k8s-all ~]# VERSION=1.28[root@k8s-all ~]# curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable.repo https://download.opensu

C++/《C++为什么要有静态成员函数》

摘要        本文说明了什么是静态成员变量,什么是静态成员函数的概念,讨论了访问私有静态成员变量的三个方法。得出用静态成员函数访问静态私有成员变量是最佳方法即回答了“C++为什么要有静态成员函数“的问题。 类的静态成员 我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。静态成员在类的所有对象中是

c++的静态变化!

静态成员   对于非静态成员,一个类的每个对象都自己存有一个副本,每个对象根据自己拥有的非静态的数据成员来区别于其他对象。而静态成员则解决了同一个类的多个对象之间数据和函数的共享问题。   静态数据成员   静态数据成员的作用是:实现同一类的不同对象之间的数据共享。   #include<IOSTREAM>   using namespace std;   class Po