runtime术语
1、Class、id
#if !OBJC_TYPES_DEFINED
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
#endif
Class是一个指向objc_class结构体的指针;id是一个指向objc_object结构体的指针;而objc_object中的isa指针指向Class结构
2、objc_class
typedef struct objc_class *Class;
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY; // metaclass
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父类
const char *name OBJC2_UNAVAILABLE; // 类名
long version OBJC2_UNAVAILABLE; // 类的版本信息,默认为0,可以通过runtime函数class_setVersion或者class_getVersion进行修改、读取
long info OBJC2_UNAVAILABLE; // 类信息,供运行时期使用的一些位标识,如CLS_CLASS (0x1L) 表示该类为普通 class,其中包含实例方法和变量;CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法;
long instance_size OBJC2_UNAVAILABLE; // 该类的实例变量大小(包括从父类继承下来的实例变量)
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 该类的成员变量地址列表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法地址列表,与 info 的一些标志位有关,如CLS_CLASS (0x1L),则存储实例方法,如CLS_META (0x2L),则存储类方法;
struct objc_cache *cache OBJC2_UNAVAILABLE; // 缓存最近使用的方法地址,用于提升效率;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 存储该类声明遵守的协议的列表
#endif
}/* Use `Class` instead of `struct objc_class *` */
从以上类objc_class的结构可以看出,类和对象的区别是类比对象多了很多特征成员,类也可以看成objc_object,即分别称作类对象和实例对象。
isa: 实例对象objc_object中的isa指针指向的是类结构objc_class,其中存放着普通成员变量与动态方法(“-”开头的方法);而objc_class中的isa指针指向类对应的元类(metaclass),metaclass中存放着static类型的成员变量与static类型的方法(“+”开头的方法).
3、SEL
SEL是selector在OC中的表示类型,selector可以理解为区别方法的ID
typedef struct objc_selector *SEL;
objc_selector的定义如下:
struct objc_selector {
char *name; OBJC2_UNAVAILABLE;// 名称
char *types; OBJC2_UNAVAILABLE;// 类型
};
4、IMP
typedef id (*IMP)(id,SEL,……);
IMP是”implementtation”缩写,它是有编译器生成的一个函数指针。当调用一个消息后,IMP决定了最终执行代码的入口。
5、Method代表类中的某个方法的类型
typedef struct objc_method *Method;
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE; // 方法名
char *method_types OBJC2_UNAVAILABLE; // 方法类型
IMP method_imp OBJC2_UNAVAILABLE; // 方法实现
}
6、Ivar代表类中实例变量的类型
typedef struct objc_ivar *Ivar;
struct objc_ivar {
char *ivar_name OBJC2_UNAVAILABLE; // 变量名
char *ivar_type OBJC2_UNAVAILABLE; // 变量类型
int ivar_offset OBJC2_UNAVAILABLE; // 基地址偏移字节
#ifdef __LP64__
int space OBJC2_UNAVAILABLE; // 占用空间
#endif
}
7、objc_property_t属性
typedef struct objc_property *objc_property_t;
objc_property是内置的类型,与之关联的还有一个objc_property_attribute_t,它是属性的attribute,也就是其实是对属性的详细描述,包括属性名称、属性编码类型、原子类型/非原子类型等。它的定义如下:
typedef struct {
const char *name; // 名称
const char *value; // 值(通常是空的)
} objc_property_attribute_t;
8、Catagory
这个就是我们平时所说的类别了,很熟悉吧。它可以动态的为已存在的类添加新的方法。
它的定义如下:
typedef struct objc_category *Category;
struct objc_category {
char *category_name OBJC2_UNAVAILABLE; // 类别名称
char *class_name OBJC2_UNAVAILABLE; // 类名
struct objc_method_list *instance_methods OBJC2_UNAVAILABLE; // 实例方法列表
struct objc_method_list *class_methods OBJC2_UNAVAILABLE; // 类方法列表
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 协议列表
}
常见方法
1、获取属性的列表—-class_copyPropertyList()
objc_property_t *propertyList = class_copyPropetyList([self class],&count);
2、获取方法列表-class_copyMethodList()
Method *methodList = class_copyMethodList([self class],&count);
3、获取成员变量列表-class_copyIvarList()
Ivar *ivarList = class_copyIvarList([self class],&count);
4、获取协议列表-class_copyProtocolList
__unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class],&count);
5、获取方法-objc_getClass
现有一个Person类,和person创建的xiaoming对象,有test1和test2两个方法
获得类方法
Class PersonClass = object_getClass([Person class]);
SEL oriSEL = @selector(test1);
Method oriMethod = class_getInstanceMethod(xiaomingClass, oriSEL);
获得实例方法
Class PersonClass = object_getClass([xiaoming class]);
SEL oriSEL = @selector(test2);
Method cusMethod = class_getInstanceMethod(xiaomingClass, oriSEL);
6、添加方法
BOOL adduce = class_addMethod(xiaomingClass,oriSEL,method_getImplementation(cusMethod),method_getTypeEncoding(cusMethod));
7、替换原方法实现
class_replaceMethod(toolClass,cusSel,Method_getImplementation(oriMethod),method_getTypeEncoding(oriMethod));
8、交换两个方法
method_exchangeImplementation(oriMethod,cusMethod);
常见作用
动态添加对象的成员变量和方法
动态交换两个方法的实现
拦截并替换方法
在方法上增加额外的功能
实现NSCoding的自动归档和解档
实现字典转模型的自动转换
runtime的消息传递
(1)发送消息涉及的函数
在OC程序中向一个对象发送消息,编译时会转换成runtime的objc_msgSend函数的调用,
例如某实例变量receiver实现oneMethod的函数调用
[receiver oneMethod];
会转换成类似一下的代码
objc_msgSend[receiver,selector];
实际上Runtime会根据类型自动转换成下列的某一函数
objc_msgSend:普通的消息都会通过该函数发送
objc_msgSend_stret:消息中有返回值(不是简单值)时,通过该函数发送消息,并接受返回值
objc_msgSendSuper:和objc_msgSend类似,把消息发送给父类的实例
objc_msgSendSuper_stret和objc_msgSend_stret类似:消息发型给父类的实例并接受返回值
(2)objc_msgSend函数的调用过程
第一:检查这个selector是不是要忽略的
第二:检查这个target是不是nil对象,nil对象发送的任意消息都会被忽略
第三:当调用实例方法时,它会根据自身的isa指针指向的类(class)的methodLists中查找该方法,如果找不到则会通过class的super_class指针找到父类,进而在父类中methodLists中查找该方法,如果还是找不到,则继续通过父类的super_class指针找到父类的父类,直到根类
当调用类方法时,它会通过自己的isa指针找到metaclass,并在metaclass中的methodLists中查找该方法,如果找不到则会通过metaclass中的super_calss指针找到其父类,然后在父类的methodLists中查找该方法,如果还是找不到,则继续通过父类的super_class指针找到父类的父类,直到根类metaclass
第四:如果第三步还是找不到就会进入动态方法解析
*通过resolveInstanceMethod:方法决定是否动态添加方法。如果返回YES,则通过class_addMethod动态添加方法,使消息得到处理;如果返回NO,则进入下一步。
*这一步进入forwordingTargetForSelector:方法,用于指定备选对象来响应这个selector(不能指定self),如果返回某个对象则会调用该对象的方法,结束;如果返回nil,则进入下一步
*这一步会调用methodSignatureForSelector:方法签名,如果返回nil,则消息无法处理;如果返回methodSignature,则调用forwordInvocation:方法,我们可以通过anInvocation对象修改实现方法、修改响应对象等,如果调用成功,则结束。如果失败,进入doseNotRecognizeSelector方法,如果没有实现该方法,就会crash
学习时的Demo:https://github.com/onebutterflyW/runtime
参考网址:https://www.ianisme.com/ios/2019.html
http://yulingtianxia.com/blog/2014/11/05/objective-c-runtime/#SEL