随笔 iOS动态解析和消息转发实现和源码分析

当一个对象调用一个方法,会在底层调用objc_msgSend()方法,如通过isa指针指向类们在方法缓存中查找,如果方法是有序的那么通过二分查找进行,未排序那就一般遍历查询。如果类中找不到会在父类中查找直到元类中;如果这里都没法找到,那就进行消息转发;消息转发我的理解是,尽最大努力让该方法去执行。

001 看是否能接受该SEL

resolveInstanceMethod&&resolveClassMethod

如果可以接受该SEL那么就将该方法转移到在该类中某方法

如果不可以接受那么就找那个target进行执行

002 为该selector找到对应的target来执行 

forwardingTargetForSelector返回一个ID类型

003 如果没有target来执行那就需要手动添加方法签名

methodSignatureForSelector&&forwardInvocation

具体代码如下:

 

//

//  TestMessage.m

//  LearnMsgForwarding

//

//  Created by maochengfang on 2020/10/22.

//

 

#import "TestMessage.h"

#import "RuntimeTool.h"

#import "BackupTestMessage.h"

 

@implementation TestMessage

 

 

+ (BOOL)resolveClassMethod:(SEL)sel{

    

    if([NSStringFromSelector(sel) isEqualToString:@"testClassFunction"]){

        

        //在元类中添加方法

        [RuntimeTool addMethodWithClass:[RuntimeTool getMetaClassWithChildClass:[self class]] withMethodSel:sel withImpMethodSel:@selector(addClassDynamicMethod)];

        return  YES;

    }

    

    return  [super resolveClassMethod:sel];

}

 

+(BOOL)resolveInstanceMethod:(SEL)sel{

    

    if([NSStringFromSelector(sel) isEqualToString:@"testFunction"]){

        

//        //给类对象添加方法

        [RuntimeTool addMethodWithClass:[self class] withMethodSel:sel withImpMethodSel:@selector(addDynamicMethod)];

        

        return  YES;

    }

    

    return  [super resolveInstanceMethod:sel];

}

 

-(id)forwardingTargetForSelector:(SEL)aSelector{

    

    if([NSStringFromSelector(aSelector) isEqualToString:@"testFunction"]){

 

        return [BackupTestMessage new];

    }

    

    return [super forwardingTargetForSelector:aSelector];

}

 

 

-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{

    

    if([super methodSignatureForSelector:aSelector] == nil){

        NSMethodSignature * sign = [NSMethodSignature signatureWithObjCTypes:"v@:"];

        return  sign;

    }

    

    return  [super methodSignatureForSelector:aSelector];

}

 

 

-(void)forwardInvocation:(NSInvocation *)anInvocation{

    

    BackupTestMessage *backup= [BackupTestMessage new];

    

    SEL sel = anInvocation.selector;

    if([backup respondsToSelector:sel]){

        [anInvocation invokeWithTarget:backup];

    }else{

        [self doesNotRecognizeSelector:sel];

    }

    

}

 

 

+ (void)addClassDynamicMethod{

    NSLog(@"动态添加类方法");

}

 

-(void)addDynamicMethod{

    NSLog(@"动态添加实例方法");

}

 

@end

 

 

 

//

//  RuntimeTool.m

//  LearnMsgForwarding

//

//  Created by maochengfang on 2020/10/22.

//

 

#import "RuntimeTool.h"

 

#import <objc/runtime.h>

 

@implementation RuntimeTool

 

+(Class)getMetaClassWithChildClass:(Class)childClass{

    //转换字符串类别

    const  char * classChar = [NSStringFromClass(childClass) UTF8String];

    //需要char的字符串 获取元类

    return objc_getMetaClass(classChar);

}

 

+ (void)addMethodWithClass:(Class)class withMethodSel:(SEL)methodSel withImpMethodSel:(SEL)impMethodSel{

    

    //获取实现的方法内容

    Method funtionRealMethod = class_getInstanceMethod(class, impMethodSel);

    //实现方法的IMP

    IMP methodIMP = method_getImplementation(funtionRealMethod);

    //实现方法的类别 返回类型 携带参数 等

    const char * types = method_getTypeEncoding(funtionRealMethod);

    //对目标添加方法

    class_addMethod(class, methodSel, methodIMP, types);

}

 

@end

 

 

 

 

//

//  BackupTestMessage.m

//  LearnMsgForwarding

//

//  Created by maochengfang on 2020/10/22.

//

 

#import "BackupTestMessage.h"

 

@implementation BackupTestMessage

 

 

-(void)testFunction{

    

    NSLog(@"备用类的对象方法testFunction");

}

 

+(void)testClassFunction{

    NSLog(@"备用类的类方法testClassFunction");

}

@end

二、源码分析

resolveClassMethod 会从元类对象执行objc_msgSend,执行本方法

  BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;

  bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);

查询是否有实现IMP

  IMP imp = lookUpImpOrNil(inst, sel, cls);

 

/***********************************************************************

* resolveClassMethod

* Call +resolveClassMethod, looking for a method to be added to class cls.

* cls should be a metaclass.

* Does not check if the method already exists.

**********************************************************************/

static void resolveClassMethod(id inst, SEL sel, Class cls)

{

    runtimeLock.assertUnlocked();

    ASSERT(cls->isRealized());

    ASSERT(cls->isMetaClass());

    if (!lookUpImpOrNil(inst, @selector(resolveClassMethod:), cls)) {

        // Resolver not implemented.

        return;

    }

 

    Class nonmeta;

    {

        mutex_locker_t lock(runtimeLock);

        nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);

        // +initialize path should have realized nonmeta already

        if (!nonmeta->isRealized()) {

            _objc_fatal("nonmeta class %s (%p) unexpectedly not realized",

                        nonmeta->nameForLogging(), nonmeta);

        }

    }

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;

    bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);

    // Cache the result (good or bad) so the resolver doesn't fire next time.

    // +resolveClassMethod adds to self->ISA() a.k.a. cls

    IMP imp = lookUpImpOrNil(inst, sel, cls);

 

    if (resolved  &&  PrintResolving) {

        if (imp) {

            _objc_inform("RESOLVE: method %c[%s %s] "

                         "dynamically resolved to %p",

                         cls->isMetaClass() ? '+' : '-',

                         cls->nameForLogging(), sel_getName(sel), imp);

        }

        else {

            // Method resolver didn't add anything?

            _objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"

                         ", but no new implementation of %c[%s %s] was found",

                         cls->nameForLogging(), sel_getName(sel),

                         cls->isMetaClass() ? '+' : '-',

                         cls->nameForLogging(), sel_getName(sel));

        }

    }

}

resolveInstanceMethod方法就在类方法中通过objc_msgSend方法执行resolveInstanceMethod

 

/***********************************************************************

* resolveInstanceMethod

* Call +resolveInstanceMethod, looking for a method to be added to class cls.

* cls may be a metaclass or a non-meta class.

* Does not check if the method already exists.

**********************************************************************/

static void resolveInstanceMethod(id inst, SEL sel, Class cls)

{

    runtimeLock.assertUnlocked();

    ASSERT(cls->isRealized());

    SEL resolve_sel = @selector(resolveInstanceMethod:);

 

    if (!lookUpImpOrNil(cls, resolve_sel, cls->ISA())) {

        // Resolver not implemented.

        return;

    }

 

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;

    bool resolved = msg(cls, resolve_sel, sel);

 

    // Cache the result (good or bad) so the resolver doesn't fire next time.

    // +resolveInstanceMethod adds to self a.k.a. cls

    IMP imp = lookUpImpOrNil(inst, sel, cls);

 

    if (resolved  &&  PrintResolving) {

        if (imp) {

            _objc_inform("RESOLVE: method %c[%s %s] "

                         "dynamically resolved to %p",

                         cls->isMetaClass() ? '+' : '-',

                         cls->nameForLogging(), sel_getName(sel), imp);

        }

        else {

            // Method resolver didn't add anything?

            _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"

                         ", but no new implementation of %c[%s %s] was found",

                         cls->nameForLogging(), sel_getName(sel),

                         cls->isMetaClass() ? '+' : '-',

                         cls->nameForLogging(), sel_getName(sel));

        }

    }

}

检查IMP是否为空对象

static inline IMP

lookUpImpOrNil(id obj, SEL sel, Class cls, int behavior = 0)

{

    return lookUpImpOrForward(obj, sel, cls, behavior | LOOKUP_CACHE | LOOKUP_NIL);

}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值