Building Objective-C static libraries with categories
Q: How do I fix "selector not recognized" runtime exceptions when trying to use category methods from a static library?
如何解决 在 runtime 环境中尝试调用静态库中分类的方法时,报 "selector not recognized" 异常?
今天在浏览 YYKit 库时,一个宏定义引起了我的注意:YYSYNTH_DUMMY_CLASS
,从字面意思看是定义了一个不起任何作用的哑类。相关描述如下:
/**
Add this macro before each category implementation, so we don't have to use
-all_load or -force_load to load object files from static libraries that only
contain categories and no classes.
More info: http://developer.apple.com/library/mac/#qa/qa2006/qa1490.html .
*******************************************************************************
Example:
YYSYNTH_DUMMY_CLASS(NSString_YYAdd)
*/
#ifndef YYSYNTH_DUMMY_CLASS
#define YYSYNTH_DUMMY_CLASS(_name_) \
@interface YYSYNTH_DUMMY_CLASS_ ## _name_ : NSObject @end \
@implementation YYSYNTH_DUMMY_CLASS_ ## _name_ @end
#endif
根据 YYKit 作者的描述,在分类中加一个这样的宏定义可以避免一些复杂操作,具体原理作者并没有解释,只是给了一个苹果官方开发文档的连接。这个链接可能因为官方整理文档的原因,URL路径已经失效了。但是在官网搜索 URL 中的关键字找到了这篇资料。根据这篇资料在这里深入了解一下其中的原理和如何解决这类问题。
官方文档以一个问题开篇,就是上面提出的那个问题,哈哈。接下来官方文档就给出了问题的解决方法:
A: If you're seeing a "selector not recognized" runtime exception when calling a category method that is implemented in a static library, you are hitting the link-time build issue described here, and need to add the -ObjC linker flag to your project, by following these steps:
- In Xcode, choose View > Navigators > Show Project Navigator, or press ⌘1.
- Select your project under the PROJECT heading in the Project Navigator, then select the Build Settings tab.
- Scroll down to the Other Linker Flags build setting under the Linking collection, or type "Other Linker Flags" into the search bar.
- Set the value of the Other Linker Flags build setting to $(OTHER_LDFLAGS) -ObjC.
译:当你在运行时调用一个在静态库中实现的分类的方法时,遇到 "selector not recognized" 运行时异常,那么你就撞到了本篇文档中描述的所谓的 "the link-time build issue" 问题,同时你需要在你的工程中加上 -ObjC linker flag
来解决这个问题。具体步骤:(在这里就不翻译了么么哒)。
Figure 1: Modifying the Other Linker Flags build setting.
Troubleshooting
If adding the -ObjC flag isn't fixing the problem, double check that a conflicting Target build setting is not overriding it, by following the above steps, but selecting the current target under "TARGETS" in step 2, instead of the project.
解决问题
如果添加-ObjC flag
没能解决这个问题,检查一下确保之前的操作没有与 Target build setting 冲突而使其无效。按照上面的步骤,在第二步时选择 "TARGETS"下的target,而不是选择 project。
Other Causes of selector not recognized Exceptions
The most common causes of a "selector not recognized" exception are:
No Such Method
The method really does not exist. Check your spelling. Check documentation to verify that the method exists on the version of the operating system your app is using.
Memory Management
Your app is trying to use an object after it has been deallocated, use the Zombies instrument to debug this kind of problem. You are seeing "selector not recognized" because the memory has been re-allocated as a different kind of object.
导致selector not recognized Exceptions
异常的其他原因
导致selector not recognized Exceptions
异常的原因通常是:
方法不存在
方法实际上是不存在的。检查你的拼写是否有误。查阅文档,确保你调用的方法在当前版本的操作系统内存在。
内存管理
在对象已释放后,你的应用又尝试去访问它,原先存放对象的内存已被重新分配给其他类的对象,所以当你再次访问原先的对象时,就会发生 "selector not recognized"异常。使用Zombies instrument
工具来debug这类问题。(这一段文档不通顺,稍作整理)
What causes those exceptions?
An impedance mismatch between UNIX static libraries and the dynamic nature of Objective-C can cause category methods in static libraries to not be linked into an app, resulting in "selector not recognized" exceptions when the methods aren't found at runtime.
导致这些异常的原因
由于 UNIX static libraries
和 dynamic nature of Objective-C
不是很搭的原因,导致静态库中分类的方法不会被链接到app中去,以至于在 runtime 动态调用时无法找到方法,结果发生 "selector not recognized"异常。
The Linker
When a C program is compiled, each "source file" is turned into an "object file" that contains executable functions and static data. The linker glues these object files together into a final executable. That executable is eventually bundled into an app by Xcode.
链接器
当一个 C 程序被编译完成,所有"原文件"都被转换为"object file",这些"object file"包含可执行方法和静态数据。链接器将这些"object file"粘合在一起,最终成为可执行文件。最终 Xcode 将这些可执行文件打包进APP中。
When a source file uses something (like a function) defined in another file, then an undefined symbol is written into the object file, to "stand in" for the missing thing. The linker resolves these symbols by pulling in the object files that include definitions of undefined symbols when building the final executable.
当一个原文件调用某些在其他文件中定义的方法或者数据时,那么一个未经定义的符号
就被写入了 "object file",用以代替缺失的方法或数据。在组成最终可执行文件时,链接器从那些包含 "undefined symbols" 定义的 "object files" 中抽取定义来解析这些符号。
For example, if main.c uses the function foo(), where foo is defined in another file, B.c, then the object file main.o will have an unresolved symbol for foo(), and B.o will include an implementation of foo(). At link time, B.o will be brought into the final executable, so that the code in main.o now references the implementation of foo() defined in B.o.
举个例子,假如 main.c
调用方法 foo()
,而 foo
函数是在另一个文件 B.c
中定义的,那么 object file main.o
将会有一个未解析符号表示 foo()
,同时 B.o
将会包含 foo()
方法实现。在链接阶段,B.o
将会被带入 最终执行文件,因此现在 main.o
中的代码就可以引用在 B.o
中对 foo()
的实现。
A UNIX static library is just a collection of object files. Normally the linker only pulls in an object file from a static library if doing so would resolve some undefined symbol. Not pulling in all object files reduces the size of the final executable.
一个 UNIX static library
只是将 object files
集合在一起。链接器通常只会从static library
中提取那些包含undefined symbol
解析的 object file,而不是把所有 object files
都集成进来。这样可以减小最终可执行文件的体积。
Objective-C
The dynamic nature of Objective-C complicates things slightly. Because the code that implements a method is not determined until the method is actually called, Objective-C does not define linker symbols for methods. Linker symbols are only defined for classes.
Objective-C
O-C 的动态特性使这个过程略微复杂了一些。因为实现一个方法的代码直到这个方法被调用时才会被确定下来。Objective-C 并不为方法定义链接符号,链接符号只为 类 进行定义。
For example, if main.m includes the code [[FooClass alloc] initWithBar:nil]; then main.o will contain an undefined symbol for FooClass, but no linker symbols for the -initWithBar: method will be in main.o.
比如: 假如 main.m 包含了这样的代码:[[FooClass alloc] initWithBar:nil];
,那么 main.o
将会包含一个未定义符号表示 FooClass
,但是在 main.o
文件中,并不生成链接符号去表示 -initWithBar:
方法。
Since categories are a collection of methods, using a category's method does not generate an undefined symbol. This means the linker does not know to load an object file defining the category, if the class itself is already defined. This causes the same "selector not recognized" runtime exception you would see for any unimplemented method.
因为分类是一组方法的集合,假如一个类已经定义了,在其中使用分类的方法并不生成 undefined symbol
,这意味着链接器不知道需要去加载对应的 object file
去定义分类。调用任何未实现的方法也一样会导致 "selector not recognized" 运行时异常。
The -ObjC Linker Flag
Passing the -ObjC option to the linker causes it to load all members of static libraries that implement any Objective-C class or category. This will pickup any category method implementations. But it can make the resulting executable larger, and may pickup unnecessary objects. For this reason it is not on by default.
The -ObjC Linker Flag
指定 链接器的 -ObjC
选项 可以使 链接器加载所有静态库总的成员,包括 Objective-C
的类和分类。这将选入所有分类方法的实现。同时也使最最终的可执行文件体积更大,这也是为什么不作为默认选项。
总结
YYKit 中的这个宏定义在 分类之前加了一个空类,这样.m
文件就包含了类,这样生成的 .o
文件就会被链接器打包进最终的可执行文件,从而避免了 "selector not recognized"异常的出现。
Building Objective-C static libraries with categories的更多相关文章
- Building Objective-C static libraries with categories(ObjC、all_load、force_load)
https://developer.apple.com/library/mac/qa/qa1490/_index.html 之所以使用该标志,和Objective-C的一个重要特性:类别(cat ...
- Using Open Source Static Libraries in Xcode 4
Using Open Source Static Libraries in Xcode 4 Xcode 4.0.1 allows us to more easily create and use th ...
- 「操作系统」:Linker Use static Libraries
While static libraries are useful and essential tools, they are also a source of confusion to progra ...
- Creating and Using Static Libraries for iPhone using Xcode 4.3
Recently, after developing a collection of applications for iPhone that were intended to be used as ...
- Openssl - Static libraries (w32, mingw) 以及对Qt静态编译时的设置
Openssl static libraries created for Windows 32bit using MinGW compiler Compiled with: ./Con ...
- (iOS)谈谈关于使用category的静态库(原创)
最近在一个项目中使用了一个包含catecategory 的静态库,但是此项目在运行过程中,该静态库调用 category 增加的方法处,却报 selector not recognized 异常,会直 ...
- Android NDK开发指南---Application.mk文件和android.mk文件
https://android.googlesource.com/platform/development/+/donut-release/ndk/docs/OVERVIEW.TXT https:// ...
- Android NDK 学习之Application.mk
Application.mk file syntax specification Introduction: This document describes the syntax of Applica ...
- Objective-C categories in static library
ASK: Can you guide me how to properly link static library to iphone project. I use staic library pro ...
随机推荐
- CentOS6非root用户下安装及配置CDH5.3.0
#install lsb packagesudo yum install -y redhat-lsb #install net-tools package sudo yum install -y ne ...
- JAVA获取树形结构
package com.nnmzkj.common.dto; import lombok.Data; import java.io.Serializable;import java.util.Arra ...
- Could not find com.android.tools.build:gradle:3.3.0.
导入新项目时报错: Error:Could not find com.android.tools.build:gradle:3.3.0. Searched in the following locat ...
- 浅入深出ETCD之【集群部署与golang客户端使用】
前言 之前说了etcd的简介,命令行使用,一些基本原理.这次来说说现实一点的集群部署和golang版本的客户端使用.因为在实际使用过程中,etcd的节点肯定是需要2N+1个进行部署的,所以有必要说明一 ...
- 运行应用-使用Deployment运行无状态应用程序
以下介绍如何使用kubernetes的deployment对象运行应用程序. 目标 - 创建nginx deployment. - 使用kubectl列出有关部署的信息. - 更新部署 创建和探索ng ...
- 全基因组关联分析学习资料(GWAS tutorial)
前言 很多人问我有没有关于全基因组关联分析(GWAS)原理的书籍或者文章推荐. 其实我个人觉得,做这个分析,先从跑流程开始,再去看原理. 为什么这么说呢,因为对于初学者来说,跑流程就像一个大黑洞,学习 ...
- Oracle 实现表中id字段自增长
Oracle 实现表中id字段自增长 最近正在学习Oracle的时候发现Oracle表中的字段不能像mysql中那样可以用auto increment修饰字段从而让id这种主键字段实现自增长. 那Or ...
- 安装 mysql-5.6.41-winx64
REF:https://www.cnblogs.com/cwb292/p/9888039.html https://dev.mysql.com/get/Downloads/MySQLInstaller ...
- 【Docker学习之三】Docker查找拉取镜像、启动容器、容器使用
环境 docker-ce-19.03.1-3.el7.x86_64 CentOS 7 一.查找.拉取镜像.启动容器1.查找镜像-docker search默认查找Docker Hub上的镜像,举例:D ...
- 读Secrets of the JavaScript Ninja(一)函数
理解JavaScript为什么应该作为函数式 在JavaScript中,函数是程序执行过程中的主要模块单元 函数是第一类对象 通过字面量创建 function ninjaFunction(){} 赋值 ...