runtime消息发送机制-动态添加方法
2019-05-18 本文已影响42人
yahibo
一、消息发送机制
主要使用objc_msgSend()函数:
objc_msgSend(id _Nullable self, SEL _Nonnull op, …)
第一个参数self:某一个对象或者某个类(类也是一个对象一个指针);
第二个参数sel:函数指针指向第一个参数(对象或类)对应的函数;
第三个及往后的参数:第二个参数(函数)对应的参数值。
主要使用函数:
1、通过字符串获取一个Class类
objc_getClass("People");
等价于:Class NSClassFromString(@"People");
2、通过方法的字符串形式获取类方法或实列方法SEL
sel_registerName("alloc");
等价于:SEL NSSelectorFromString(@"alloc");
等价于:SEL @selector(alloc);
1、使用消息发送机制创建对象调用方法:
People *p = objc_msgSend(objc_getClass("People"), sel_registerName("alloc"));
p = objc_msgSend(p, sel_registerName("init"));
objc_msgSend(p, sel_registerName("eat"));
//发送参数
objc_msgSend(p, sel_registerName("eat:what:"),@"吃",@"鱼");
2、进入项目文件中使用clang(c、c++、oc的轻量级编译器)重写main.m文件会生成main.cpp文件,即OC对应的c语言代码
//终端进入文件中执行clang命令:
clang -rewrite-objc main.m
OC代码:
#import <Foundation/Foundation.h>
#import "People.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
People *p = [[People alloc] init];
[p performSelector:@selector(eat)];
}
return 0;
}
C代码:
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
People *p = ((People *(*)(id, SEL))(void *)objc_msgSend)((id)((People *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("People"), sel_registerName("alloc")), sel_registerName("init"));
((id (*)(id, SEL, SEL))(void *)objc_msgSend)((id)p, sel_registerName("performSelector:"), sel_registerName("eat"));
}
return 0;
}
观察以上代码可知,对象创建方法调用,均通过消息发送机制完成。同时可以看出OC代码最终编译成C语言对应的结构体、函数等类型。
二、动态添加方法
主要使用class_addMethod()函数:
class_addMethod(Class _Nullable __unsafe_unretained cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types)
第一个参数cls:当前对象的类;
第二个参数SEL name:指当前调用的实列方法;
第三个参数imp:函数名称或函数指针指向当前类中添加的函数;
第四个参数types:对应的参数可写""或"v@:@"。
主要使用函数:
1、通过字符串获取一个Class类
objc_getClass("People");
等价于:Class NSClassFromString(@"People");
2、通过方法的字符串形式获取类方法或实列方法SEL
sel_registerName("alloc");
等价于:SEL NSSelectorFromString(@"alloc");
等价于:SEL @selector(alloc);
通过消息发送机制发送消息,调用的函数不存在系统会crash报错
并且会调用resolveInstanceMethod方法:
+(BOOL)resolveInstanceMethod:(SEL)sel;
因此可以在该方法内部添加对应的方法作为提示
//没有找到方法会调用该方法
+(BOOL)resolveInstanceMethod:(SEL)sel{
//强制转换去除警告
class_addMethod(self.class, sel, (IMP)noMethod, "");
return [super resolveInstanceMethod:sel];
}
//动态添加的方法
void noMethod(){
NSLog(@"没找到这个方法");
}
/*
完整写法
1、方法调用者
2、方法编号
3、方法的第一个参数
self _cmd隐式参数 每个方法都有两个隐式参数
对应:objc_msgSend(p, @selector(sleep),@"yahibo");
*/
void noMethod(id self,SEL _cmd,NSString *obj){
NSLog(@"没找到这个方法arg->%@",obj);
}
OC中的每一个方法均对应有两个隐式参数 id self和SEL _cmd第三个参数则函数对应的参数