目录
objc_msgSend
看下源码
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person * person = [[Person alloc] init];
[person test];
[Person classTest];
}
return 0;
}
执行指令:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
精简代码如下
Person * person = objc_msgSend(
objc_msgSend(objc_getClass("Person"),
sel_registerName("alloc")
),
sel_registerName("init")
);
objc_msgSend(person, sel_registerName("test"));
objc_msgSend(objc_getClass("Person"), sel_registerName("classTest"));
我们看到方法调用在底层转为了c语言函数objc_msgSend。
实例对象调用传递两个参数,实例对象本身也就是self和方法选择器sel_registerName(“test”)也就是@Selector(test)。
类对象调用传递两个参数,类对象本身和方法选择器set_registerName(“classTest”)。
以上就是OC的方法调用:消息机制,给方法调用者发送消息
objc_msgSend(person, sel_registerName("test"));
消息接收者(receiver):person
消息名称:test
objc_msgSend(objc_getClass("Person"), sel_registerName("classTest"));
消息接收者(receiver):[Person class]
消息名称:classTest
objc_msgSend的执行流程可以分为3个阶段
消息发送
动态方法解析
消息转发
如果以上三个阶段都无法处理,我们就会收到那个经典报错:
unrecognized selector sent to instance 0x1005b1670
消息发送
流程图
动态方法解析
流程图
一、对象方法的动态方法解析
Person * person = [[Person alloc] init];
[person test];
Person类
@interface Person : NSObject;
-(void)test;
@end
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
-(void)other{
NSLog(@"other-%@,%s",self,_cmd);
}
+(BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(test)) {
NSLog(@"resolveInstance");
//获取method
Method method = class_getInstanceMethod(self, @selector(other));
//动态添加方法
class_addMethod(self,
sel,
method_getImplementation(method),
method_getTypeEncoding(method));
//
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
日志打印如下
resolveInstance
other-<Person: 0x2818c41b0>,test
我们可以看出Person类并没有实现test方法,通过动态方法解析调用到了other方法里面。
添加c语言方法的代码演示
Person * person = [[Person alloc] init];
[person test];
Person类
@interface Person : NSObject;
-(void)test;
@end
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
void c_other(id self,SEL _cmd){
NSLog(@"c_other-%@,%s",self,_cmd);
}
+(BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(test)) {
NSLog(@"resolveInstance");
class_addMethod(self, sel, (IMP)c_other, "v16@0:8");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
日志打印如下
resolveInstance
c_other-<Person: 0x283bd06a0>,test
二、类方法的动态方法解析
[Person classTest];
Person类
@interface Person : NSObject;
+(void)classTest;
@end
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
+(void)classOther{
NSLog(@"classOther-%@,%s", self,_cmd);
}
+(BOOL)resolveClassMethod:(SEL)sel{
if (sel == @selector(classTest)) {
//获取类方法,传进去的是元类对象object_getClass(self)
Method method = class_getClassMethod(
object_getClass(self),
@selector(classOther));
//添加类方法,传进去的是元类对象object_getClass(self)
class_addMethod(
object_getClass(self),
sel,
method_getImplementation(method),
method_getTypeEncoding(method));
return YES;
}
return [super resolveClassMethod:sel];
}
@end
日志打印如下
classOther-Person,classTest
消息转发
流程图
消息转发代码演示
Person * person = [[Person alloc] init];
[person test];
Person类
@interface Person : NSObject;
-(void)test;
@end
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
-(id)forwardingTargetForSelector:(SEL)aSelector{
NSLog(@"%s",__func__);
if (aSelector == @selector(test)) {
Class Cat = NSClassFromString(@"Cat");
//返回一个实例对象 就会调用实例对象的test方法
//return [[Cat alloc] init];
//返回nil 就会调用methodSignatureForSelector
return nil;
}
return [super forwardingTargetForSelector:aSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSLog(@"%s",__func__);
if (aSelector == @selector(test)) {
//注册消息转发到的方法的类型
return [NSMethodSignature signatureWithObjCTypes:"i20@0:8i:4"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"%s",__func__);
//anInvocation 包含selector targe argument
if (anInvocation.selector == @selector(test)) {
Class Cat = NSClassFromString(@"Cat");
id cat = [[Cat alloc] init];
//更改消息接收者
anInvocation.target =cat;
//更改selector
anInvocation.selector = @selector(catTest:);
//设置新方法参数
int age = 5;
[anInvocation setArgument:&age atIndex:2];
//新方法调用
[anInvocation invoke];
//获取新方法的返回值
int reValue;
[anInvocation getReturnValue:&reValue];
NSLog(@"reValue=%d",reValue);
}
}
@end
创建一个Cat类
-(void)test{
NSLog(@"%s",__func__);
}
-(int)catTest:(int)age{
NSLog(@"%s",__func__);
NSLog(@"age=%d",age);
return age+2;
}
日志打印如下
-[Person forwardingTargetForSelector:]
-[Person methodSignatureForSelector:]
-[Person forwardInvocation:]
-[Cat catTest:]
age=5
reValue=7
行者常至,为者常成!