XB_runtime(消息发送)

前言:

 前言1.方法的底层结构

struct method_t{
   SEL name; //函数名
   const char *types;//编码(返回值类型、参数类型)
   IMP imp;//指向函数的指针(函数地址)
}
  • SEL代表函数名,一般叫做选择器,底层结构跟char *类似

       

  1. 可以通过@selector() 合sel_registerName()获得
  2. 可以通过sel_getName()和NSStringFromSelector()转成字符串
  3. 不同类中相同名字的方法,所对应的的方法选择器是相同的 
  • IMP代表函数的具体实现 

       

  • types包含了函数的返回值、参数编码的字符串 

     iOS中提供了一个叫做@encode的指令,可以将具体的类型表示成字符串编码

     

关于typedef struct objc_method *Method;它其实等价于struct method_t;

 前言2.方法缓存cachet cache 的底层结构

Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)来缓存曾经调用过的方法,函数名为key,函数地址为value,可以提高方法的查找速度

 

实现:

1.objc_msgSend执行流程01-消息发送

在Class结构体内跟方法相关的有两个底层结构:

1>struct objc_class中的 cachet cache //方法缓存

2>struct class_rw_t中的method_list_t *methods //方法列表

消息查找的顺序是先在receiverClass的cache中查找方法是否存在,不存在的话就在receiverClass方法列表中查找,没有的话就superClass的cache中查找,如果找到了就调用方法并将方法缓存到receiverClass的cache中,如果superClass的cache中没有找到,就在superClass的方法列表中查找,如果找到了就调用方法并将方法缓存到receiverClass的cache中,如果没有就查看上层是否还有superClass。。。

2.objc_msgSend执行流程02-动态方法解析

动态方法解析分为类方法和实例方法两种模式

1>实例方法

- (void)other
{
    NSLog(@"%s---", __func__);
}

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == @selector(test)) {
        // 获取其他方法,这里是self中有other这个方法的实现,如果其他类有某个方法的实现可以改成
        //Method method = class_getInstanceMethod([XXXClass class], @selector(other));
        Method method = class_getInstanceMethod(self, @selector(other));

        // 动态添加test方法的实现
        class_addMethod(self, sel,
                        method_getImplementation(method),
                        method_getTypeEncoding(method));

        // 返回YES代表有动态添加方法
        return YES;
    }


    return [super resolveInstanceMethod:sel];
}


//由于关于typedef struct objc_method *Method;等价于struct method_t,所以可以直接类型转换

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == @selector(test)) {
        // 获取其他方法
        struct method_t *method = (struct method_t *)class_getInstanceMethod(self, @selector(other));

        // 动态添加test方法的实现
        class_addMethod(self, sel, method->imp, method->types);

        // 返回YES代表有动态添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
//也可以添加C语言函数来实现
void c_other(id self, SEL _cmd)
{
    NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
}

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == @selector(test)) {
        // 动态添加test方法的实现,v16@0:8可以简化成v@:
        class_addMethod(self, sel, (IMP)c_other, "v16@0:8");

        // 返回YES代表有动态添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

 

 2>类方法

void c_other(id self, SEL _cmd)
{
    NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
}
//
+ (BOOL)resolveClassMethod:(SEL)sel
{
    if (sel == @selector(test)) {
        // 第一个参数是object_getClass(self)
        class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
        return YES;
    }
    return [super resolveClassMethod:sel];
}

他们的用法类似,只是class_addMethod的第一个参数,一个是类,一个是元类,个人理解是将消息拦截,然后给receiver动态添加方法的实现。

3.objc_msgSend执行流程03-消息转发

如果没有实现消息解析,就进入消息转发阶段

+ (id)forwardingTargetForSelector:(SEL)aSelector
{
    // objc_msgSend([[Cat alloc] init], @selector(test))
    // [[[Cat alloc] init] test]
    if (aSelector == @selector(test)) return [[Cat alloc] init];

  //如果return的是[Cat class],那么底层就是调用转发目标的类方法
  // objc_msgSend([Cat class], @selector(test))
    // [Cat test]
    return [super forwardingTargetForSelector:aSelector];
}

+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    if (aSelector == @selector(test)) return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    
    return [super methodSignatureForSelector:aSelector];
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation
{
    NSLog(@"1123");
}

@end

生成方法签名还可以利用某个类的方法声明来实现

[[[XXXClass alloc] init] methodSignatureForSelector:aSelector];

至于+ (void)forwardInvocation:(NSInvocation *)anInvocation;

既可以什么都不做,也可以到用invocation指定的目标中找方法的实现。

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    // 参数顺序:receiver、selector、other arguments
//    int age;
//    [anInvocation getArgument:&age atIndex:2];
//    NSLog(@"%d", age + 10);
    
    
    // anInvocation.target == [[Cat alloc] init]
    // anInvocation.selector == test:
    // anInvocation的参数:15
    // [[[Cat alloc] init] test:15]
    
    [anInvocation invokeWithTarget:[[Cat alloc] init]];
    
    int ret;
    [anInvocation getReturnValue:&ret];
    
    NSLog(@"%d", ret);
}

消息转发阶段会再次执行objc_msgSend的第一步!!!

 

补充:super的本质

super调用,底层会转换成objc_msgSendSuper2函数的调用,接收2个参数

1.>struct objc_super2

2.>SEL

而objc_super2的结构是

struct objc_super2{
   id receiver;
   CLass current_class;
}

reveicer是消息接受者,current_class是receiver的Class对象。这里可以解释[self class]与[super class]的区别。super调用仅仅表明查找方法是从父类开始的而已。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值