美文网首页
Runtime方法调用过程分析

Runtime方法调用过程分析

作者: 天蚕 | 来源:发表于2017-06-22 15:53 被阅读137次

方法调用过程分析流程图

动态添加方法.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方法调用过程分析

    方法调用过程分析流程图 文字描述:1、从当前类反向遍历继承树,直到NSObject,如果没有找到了,就直接执行,到...

  • runtime的实用性讲解

    runtime 概念 runtime 运行时机制,主要是消息机制。OC 中的方法调用属于消息的发送,动态调用过程,...

  • iOS消息发送的调用过程

    iOS方法的调用过程 [object methodA]runtime层会将这个方法翻译成objc_msgSend(...

  • OC -> Runtime

    Runtime简介 Runtime用处 Runtime实践 Runtime 类方法调用实现。Person * p ...

  • iOS load

    +load方法会在runtime加载类分类时,调用程序一启动就会调用.每个类的,分类的load方法,在程序运行过程...

  • load 和 initialize

    load +load 方法会在 runtime 加载类、分类是调用,每个类、分类的 load 方法,在程序运行过程...

  • Runtime源码 方法调用的过程

    前言 Objective-C语言的一大特性就是动态的,根据官方文档的描述:在runtime之前,消息和方法并不是绑...

  • Runtime源码 —— 方法调用的过程

    在写这篇文章之前,我关于方法调用的知识是比较零散的,甚至一度以为消息转发就是方法调用的过程。现有的文章大多根据苹果...

  • 方法调用底层实现

    runtime怎么实现方法的调用 :消息机制,runtime系统会把方法调用转化为消息发送。即objc-msgSe...

  • 通过runtime源码完整分析消息机制

    [TOC] 一、前言 本文主要分析当我们调用[p test1]的过程中,runtime是如何调用的。 本文的调试代...

网友评论

      本文标题:Runtime方法调用过程分析

      本文链接:https://www.haomeiwen.com/subject/sputcxtx.html