OC消息发送机制

news/2024/7/20 21:43:47 标签: objective-c, ios, 开发语言

OC消息发送分两步:

1.编译阶段

方法调用转换成objc_msgSend函数调用

不带参数:objc_msgSend(receiver,selector)

带参数:objc_msgSend(recevier,selector,org1,org2,…)

2.运行时阶段,

先从类到父类继承链中查找方法,没找到就消息转发

整个调用流程图如下

2.1.继承链中查找方法

实例对象调用一个方法,会首先在本类方法列表查找,如果没有,会在父类再查找,直到根类NSObject,在任何一层找到方法,则执行,如果到了最后根类NSObject还没有找到,才会触发Objective-C Runtime的消息转发机制。

类对象调用一个方法,会首先在本类的元类方法列表中查找,如果没有,会在元类的父类再查找,直到根类NSObject的元类,在任何一层找到方法,则执行,如果到了最后根类NSObject元类还没有找到,才会触发Objective-C Runtime的消息转发机制。

2.2.OC Runtime的消息转发机制

2.2.1.NSObject的消息转发机制包括动态方法解析、消息接受者重定向、消息重定向

消息转发流程如下:

1.先调用实例方法resolveInstanceMethod

如果作者在这里使用runtime动态添加对应的方法,并且返回yes。就万事大吉。对象找到了处理的方法,

并且将这个新增的方法添加到类的方法缓存列表

2.如果上面的方法返回NO的话,对象会调用forwardingTargetForSelector方法

允许作者选择其他的对象,处理这个消息。

这个方法,也是待会我们要做文章的地方。画重点。

3.如果上面两个方法都没有做处理,那么对象会执行最后一个方法methodSignatureForSelector,提供一个有效的方法签名,若提供了有效的方法签名,程序将会通过forwardInvocation方法执行签名。若没有提供方法签名就会触发doesNotRecognizeSelector方法,触发崩溃。

id proxy = [MessageSender proxyForObject1:[Dog new] object2:[Cat new]];

[proxy speak];

[proxy eat];

#import "MessageSender.h"

#import <objc/runtime.h>

@implementation Dog

- (void)speak {

    NSLog(@"Dog speak");

}

@end

@implementation Cat

- (void)eat {

    NSLog(@"Cat eat");

}

@end

@interface MessageSender()

@property (nonatomic, weak) id target1;

@property (nonatomic, weak) id target2;

@end

@implementation MessageSender

- (instancetype)initWithTarget1:(id)obj1 target2:(id)obj2 {

    _target1 = obj1;

    _target2 = obj2;

    return self;

}

+ (id)proxyForObject1:(id)obj1 object2:(id)obj2 {

    return [[MessageSender alloc] initWithTarget1:obj1 target2:obj2];

}

//动态方法解析

+ (BOOL)resolveInstanceMethod:(SEL)sel {

// 这里可以新增一个处理unknown selector的方法,

    if (sel == @selector(speak)) {

        class_addMethod([self class], sel, imp_implementationWithBlock(^(id self, NSString *str) {

            NSLog(@"block, val=%@", str);

        }), "v@");

//  class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "v@:@");

    }

    return [super resolveInstanceMethod:sel];

}

+(BOOL)resolveClassMethod:(SEL)sel {

    return NO;

}

//消息接受者重定向

- (id)forwardingTargetForSelector:(SEL)aSelector {

// 若返回一个其它的执行对象,那消息从id objc_msgSend ( id self, SEL op, ...)重新开始

    if ([_target1 respondsToSelector:aSelector]) {

        return _target1;

    }

    if ([_target2 respondsToSelector:aSelector]) {

        return _target2;

    }

    return nil;

}

//消息重定向

//重写NSProxy如下两个方法,在处理消息转发时,将消息转发给真正的Target处理

/*

    返回nil,Runtime 则会发出 -doesNotRecognizeSelector: 消息,程序 crash

    返回了NSMethodSignature,Runtime 就会创建一个 NSInvocation 对象并发送 -forwardInvocation: 消息

*/

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



    NSMethodSignature *sig;



    sig = [_target1 methodSignatureForSelector:selector];

    if (sig) {

        return sig;

    }



    sig = [_target2 methodSignatureForSelector:selector];

    return sig;

}

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

    id target;

    if ([_target1 methodSignatureForSelector:invocation.selector]) {

        target = _target1;

    }else{

        target = _target2;

    }

    [invocation invokeWithTarget:target];



}

@end

2.2.2NSProxy作为抽象类不能直接使用,它不提供初始化的方法,并且在接收到它没有响应的任何消息时引发异常,

因此只能继承并在具体的子类提供初始化或创建方法,并实现forwardInvocation:和methodSignatureForSelector:

NSProxy消息转发过程简单很多,接收到unknown selector后,直接回调

- (NSMethodSignature *)methodSignatureForSelector:和

- (void)forwardInvocation:


http://www.niftyadmin.cn/n/905314.html

相关文章

python类与类之间的关系

在面向对象中,类和类之间也可以产生相关的关系类中的关系: 依赖关系是最轻的,最重的是继承关系,关联关系是比较微妙的依赖关系执行某个动作的时候,需要xxx来帮助完成这个操作,此时的关系是最轻的.随时可以更换另外一个东西来完成此操作 1 class Person:2 def f1(self,tools…

NSTimer介绍

1.创建NSTimer 常用方法有 (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats; (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)target selector:(SE…

纪念12月21,展望2019

2015年12月21日&#xff0c;第一家公司入职&#xff1b; 2018年12月21日&#xff0c;第二家公司转正&#xff1b; 回顾三年有太多的不如意与不甘&#xff0c;愿在来年的2019可以坚定决心&#xff0c;坚定不移的一直走下去&#xff0c;为了让自己和家人生活的更好&#xff0c;以…

iOS保存图片到相册

方法1&#xff1a;用C语言函数UIImageWriteToSavedPhotosAlbum实现 //参数1:图片对象 //参数2:成功方法绑定的target //参数3:成功后调用方法 //参数4:需要传递信息(成功后调用方法的参数) UIImageWriteToSavedPhotosAlbum(self.imageView.image, self, selector(image:didFin…

计算机视觉与遥感实验室(CVRS Lab)

http://cvrs.whu.edu.cn/ 武汉大学计算机视觉与遥感实验室&#xff08;WHU-CVRS Lab&#xff09;由学校高层次引进人才姚剑教授于2012年4月创建成立&#xff0c;目前成员包括1名博士后&#xff0c;20余名博士和硕士研究生&#xff0c;10多名本科生。自从成立至今&#xff0c;CV…

2018-12-25工作记录 空白行===水晶报表

C# 多行文本去掉空白行&#xff0c;空行和重复行 string[] strArraystr.Split(new char[] { \r, \n }, StringSplitOptions.RemoveEmptyEntries); strArray strArray.GroupBy(p > p).Select(p > p.Key).ToArray(); 水晶报表&#xff1a;一般先建立一个数据集&#xff0c…

iOS 显示圆角、阴影和边框

iOS 同时显示圆角(部分)、阴影和边框 在 iOS 开发中&#xff0c;让View显示圆角和阴影以及边框 方法1&#xff1a;系统的UIView UIView *v[[UIView alloc]initWithFrame:CGRectMake(100, 200, viewWidth, viewHeight)];v.backgroundColor[UIColor yellowColor]; // v.layer.m…

Windows上提高工作效率的神器推荐

Everything 下载地址&#xff1a;http://www.voidtools.com/功能&#xff1a;硬盘文件搜索&#xff0c;比起电脑自带的文件搜索&#xff0c;效率提高不是一丁半点。而且Everything还支持正则表达式&#xff0c;小巧而快速&#xff1b;Clover 下载地址&#xff1a;http://cn.eji…