JHHK

欢迎来到我的个人网站
行者常至 为者常成

Runtime(三):消息机制

目录

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

消息发送

流程图

img

动态方法解析

流程图

img

一、对象方法的动态方法解析

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

消息转发

流程图

img

消息转发代码演示

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

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.