对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。 在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。
作用 :
1. 发送消息
- 方法调用的本质就是让对象发送消息。
- 方法调用流程:
对象的cache方法-->对象的方法列表-->父类的cache方法-->父类的方法列表 - 如果一直找不到就转向拦截调用,如果没有重写拦截调用的方法则报错!!!
2. 获取列表
包括属性列表,方法列表、成员变量列表、遵循的协议列表。
3. 交换方法
开发使用场景:系统自带的方法功能不够,给系统自带的方法扩展一些功能,并且保持原有的功能。
方式一 :继承系统的类,重写方法。
方式二 :使用runtime,交换方法。其实质就是交换两个方法的实现方式。
4. 动态添加方法
动态添加方法就是在拦截调用的方法中添加方法,使用 [target performSelector:@selector(eat)];
然后,在 target 对象内部重写拦截调用的方法,动态添加方法。
拦截调用的方法有:
// 1. 不存在类方法时调用方法,会调用这个方法,默认返回NO,你可以加上自己的处理,然后返回YES;
+ (BOOL)resolveClassMethod:(SEL)sel;
// 2. 存在实例方法时调用时会调用这个方法,默认返回NO。
+ (BOOL)resolveInstanceMethod:(SEL)sel;
// 3. 这个方法是将你调用的不存在的方法重定向到一个其他声明了这个方法的类,只需要你返回一个有这个方法的target。
- (id)forwardingTargetForSelector:(SEL)aSelector;
// 4. 将你调用的不存在的方法打包成NSInvocation传给你。做完你自己的处理后,调用invokeWithTarget: 方法让某个target触发这个 法。
- (void)forwardInvocation:(NSInvocation*)anInvocation;
5. 给分类添加属性
给分类添加属性,其实质就是利用关联对象技术为这个类添加属性。
附:
- OC中的一切对象,在 Runtime 中用结构体表示:
// 描述类中的一个方法
typedef struct objc_method *Method;
// 描述类中的实例变量
typedef struct objc_ivar *Ivar;
// 描述类中的类别Category
typedef struct objc_category *Category;
// 描述类中声明的属性
typedef struct objc_property *objc_property_t;
- OC中的类在 Runtime 中的表示:
struct objc_class {
Class isa ; // 指针,实例的isa指向类对象,类对象的isa指向元类
#if !__OBJC2__
Class super_class; // 指向父类
const char *name; // 类名
long version;
long info;
long instance_size;
struct objc_ivar_list *ivars; // 成员变量列表
struct objc_method_list **methodLists; // 方法列表
struct objc_cache *cache; // 缓存
struct objc_protocol_list *protocols; // 协议列表
#endif
} OBJC2_UNAVAILABLE;
相关Demo请参见:
Demo地址:https://github.com/Reminiscencexing/runtime1
https://github.com/Reminiscencexing/Runtime2
https://github.com/Reminiscencexing/Runtime3
https://github.com/Reminiscencexing/Runtime4