面试题答案
一键面试类别(Category)在运行时加载方法过程
- 编译阶段:编译器将Category的方法、属性、协议信息等收集起来,放到一个结构体
category_t
中。 - 加载阶段:运行时,Runtime系统会将Category的信息合并到类的相关数据结构中。具体来说,Category的方法会被添加到类的方法列表头部。这样做的原因是,Category通常用于为已有类添加新功能,放在头部能让新添加的方法优先被调用。
- 消息传递阶段:当发送一个消息给对象时,Runtime会根据对象的isa指针找到对应的类。在类的方法列表中查找方法实现。由于Category的方法在方法列表头部,所以会优先查找Category中实现的方法,如果找到就执行相应的实现代码。
相关的数据结构
- category_t:这是Category在运行时的核心数据结构,定义如下:
struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
其中,name
是Category的名称,cls
指向被扩展的类,instanceMethods
和classMethods
分别是实例方法列表和类方法列表,protocols
是协议列表,instanceProperties
是实例属性列表。
2. method_list_t:表示方法列表的数据结构,包含了一系列的method_t
。
struct method_list_t {
struct entsize_list_tt<method_t, method_list_t> entsizeAndFlags;
unsigned int count;
method_t *first;
};
- method_t:代表一个方法,包含方法名、方法实现、方法类型编码等信息。
struct method_t {
SEL name;
const char *types;
IMP imp;
struct SortBySELAddress :
public std::binary_function<const method_t&, const method_t&, bool>
{
bool operator() (const method_t& lhs, const method_t& rhs) const {
return lhs.name < rhs.name;
}
};
};
消息传递机制
- 当向一个对象发送消息时,Runtime首先根据对象的isa指针找到对应的类。
- 在类的方法列表(包括本类方法列表和Category添加到头部的方法列表)中查找与消息对应的方法实现(通过SEL查找IMP)。如果在本类中没有找到,会沿着继承体系向上查找。
- 如果最终都没有找到方法实现,会进入动态方法解析阶段,尝试动态添加方法。若动态方法解析也无法处理,会进入备用接收者阶段,看是否有其他对象能处理该消息。若仍然无法处理,最后会进入消息转发阶段,通过完整的消息转发流程来处理消息,否则程序会崩溃。