一、消息发送机制
主要使用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);
注意:在项目中使用runtime
库中对应方法,需要在TARGETS>Build Settings>objc_msgSend Calls
处设置为No
。
或者使用一下写法:
((void(*)(id,SEL))objc_msgSend)(self, @selector(setNumber:));
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);
通过消息发送机制发送消息,,调用的函数不存在系统会调用resolveInstanceMethod
方法,并且会crash
报错。
+(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
第三个参数则函数对应的参数。