Runtime方法调用过程分析

方法调用过程分析流程图

Runtime方法调用过程分析_第1张图片
动态添加方法.png

文字描述:
1、从当前类反向遍历继承树,直到NSObject,如果没有找到了,就直接执行,到此调用流程结束,这是正常流程
2、当继承树中没有找到方法实现,进入非正常调用流程(重定向和消息转发)
3、此时可以重写决议方法,在决议方法中动态添加方法实现,一旦添加成功,系统自动跳转执行动态添加的方法实现,到此调用流程结束
4、如果没有重写决议方法或者方法中没有动态添加方法方法实现,首先尝试进入重定向流程(重定向流程标志重定向方法,且返回不是self或者nil)+ (BOOL)resolveInstanceMethod:(SEL)sel+ (BOOL)resolveClassMethod:(SEL)sel
5、如果重定重定向流程(重定向方法重写的时候也可以动态添加方法实现,只不过不会自动执行,需要手动调用),且将调用重定向到另一个对象,该对象会调用自己的同名方法,流程同上,到此调用流程结束
- (id)forwardingTargetForSelector:(SEL)aSelector
6、如果没有进入重定向流程,就会自动进入到消息转发流程
7、消息转发流程首先必须要执行的是方法签名- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
8、执行完方法签名,最终执行消息转发forwardInvocation:(NSInvocation *)anInvocation
,在此方法中可以将任意修改方法,指定任意对象,执行任意可执行方法
9、如果继承树遍历,动态决议,重定向和消息转发都没有找到合适的方法执行抛出异常unrecognized selector sent to instance

一、动态添加方法实现

注意:
1、动态添加方法实现可以在上述的决议方法(方法返回自动进入执行),重定向方法和转发方法任意位置添加,推荐在决议方法中添加,流程越少越好,减少了代码执行,和未知逻辑判断,相对而言效率可定更高。
2、动态添加方法实现的区别主要在于
class_addMethod([self class], sel, testImp, "v@:");
class_addMethod(object_getClass(self), sel, testImp, "v@:");添加实例方法使用[self class],添加类方法使用object_getClass(self)
3、类方法只能通过动态添加方法实现的方式处理,消息相关的都是对象,不是类

//动态添加实例方法实现
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    NSLog(@"%@",NSStringFromSelector(sel));
    id testImpBlock = ^(id self)
    {
        NSLog(@"对象方法->%@缺少实现",NSStringFromSelector(sel));
    };
    IMP testImp = imp_implementationWithBlock(testImpBlock);
    class_addMethod([self class], sel, testImp, "v@:");
    return NO;
}

//动态添加类方法实现
+ (BOOL)resolveClassMethod:(SEL)sel
{
    id testImpBlock = ^(id self)
    {
        NSLog(@"类方法->%@缺少实现",NSStringFromSelector(sel));
    };
    IMP testImp = imp_implementationWithBlock(testImpBlock);
    
    class_addMethod(object_getClass(self), sel, testImp, "v@:");
    
    return NO;
}

二、重定向

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    return [[Student alloc] init];
}

三、消息转发

注意:
1、方法签名和消息转法必须是成对出现的
2、NSInvocationperformSelector:withObjectOC发送消息的两个方式,NSInvocation对象中包含了方法执行的对象,要执行的方法及其参数,返回值等信息,NSInvocation对象直接执行invoke或者invokeWithTarget:(id)target就是发送消息,执行方法。eg:如果在重定向中或者方法签名中添加方法实现,在消息转发中,直接用[anInvocation invoke]就可以完成方法调用,但是不推荐,还是推荐如果使用动态添加方法实现放在决议方法中

//方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSMethodSignature *sign = [NSMethodSignature signatureWithObjCTypes:"v@:"];
    return sign;
}

//消息转发
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    NSInteger count = anInvocation.methodSignature.numberOfArguments;
    NSLog(@"%@->有%zd个参数",NSStringFromSelector(anInvocation.selector),count);
    
    for (NSInteger i = 0; i < count; i++)
    {
        if (i > 1)
        {
            void *arg;
            [anInvocation getArgument:&arg atIndex:i];
            NSLog(@"%@",(__bridge id)arg);
        }
    }
    
//    [anInvocation invoke];
}

四、应用 (避免出现unrecognized selector sent to instance异常)

1、直接实现决议方法,动态添加一个空的方法实现,那么永远不会出现次异常,推荐使用,代码如下即可:

//动态添加实例方法实现
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    NSLog(@"%@",NSStringFromSelector(sel));
    id testImpBlock = ^(id self)
    {
        NSLog(@"对象方法->%@缺少实现",NSStringFromSelector(sel));
    };
    IMP testImp = imp_implementationWithBlock(testImpBlock);
    class_addMethod([self class], sel, testImp, "v@:");
    return NO;
}

//动态添加类方法实现
+ (BOOL)resolveClassMethod:(SEL)sel
{
    id testImpBlock = ^(id self)
    {
        NSLog(@"类方法->%@缺少实现",NSStringFromSelector(sel));
    };
    IMP testImp = imp_implementationWithBlock(testImpBlock);

    class_addMethod(object_getClass(self), sel, testImp, "v@:");

    return NO;
}

2、对于对象方法,重写消息转发的两个方法:

//方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSMethodSignature *sign = [NSMethodSignature signatureWithObjCTypes:"v@:"];
    return sign;
}

//消息转发
- (void)forwardInvocation:(NSInvocation *)anInvocation
{

}

3、功能宏

#define NOCRASH \
\
+ (BOOL)resolveInstanceMethod:(SEL)sel\
{\
        id testImpBlock = ^(id self)\
        {\
            NSLog(@"对象方法->%@缺少实现",NSStringFromSelector(sel));\
        };\
        IMP testImp = imp_implementationWithBlock(testImpBlock);\
        class_addMethod([self class], sel, testImp, "v@:");\
    return NO;\
}\
\
+ (BOOL)resolveClassMethod:(SEL)sel\
{\
        id testImpBlock = ^(id self)\
        {\
            NSLog(@"类方法->%@缺少实现",NSStringFromSelector(sel));\
        };\
        IMP testImp = imp_implementationWithBlock(testImpBlock);\
    \
        class_addMethod(object_getClass(self), sel, testImp, "v@:");\
    \
    return NO;\
}\

五、Block

1、block类似于函数指针,但是有时内联的(代码直接插入到调用者处,免去了普通函数调用的过程,效率更高)
2、block是一个代码片段,在OC中被看作是OC对象
3、block建议使用copy策略
4、block分为三种__NSGlobalBlock__,__NSMallocBlock__,__NSStackBlock__,MRC默认情况下是__NSGlobalBlock__,使用(捕获)了外部非static和全局的变量会变成__NSStackBlock____NSStackBlock__的block使用了copy策略,就变成__NSMallocBlock__的了,ARC情况下copy和strong都一样,系统默认会调用copy变成__NSMallocBlock__
5、在MRC和ARC下都不要使用assign策略

为什么block使用copy

1、因为MRC下不使用copy的话,如果使用了__NSStackBlock__的block,可能会出野指针
2、因为MRC下不使用copy的话,如果使用了__NSStackBlock__的block,会出现线程不安全的隐患

你可能感兴趣的:(Runtime方法调用过程分析)