Runtime方法调用过程分析

方法调用过程分析流程图

动态添加方法.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,会出现线程不安全的隐患

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,639评论 18 139
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,692评论 0 9
  • 参考链接: http://www.cnblogs.com/ioshe/p/5489086.html 简介 Runt...
    乐乐的简书阅读 2,132评论 0 9
  • Runtime是什么 Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我...
    SuAdrenine阅读 872评论 0 3
  • 转载:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麦子阅读 729评论 0 2