OC消息转发机制

OC消息转发机制

在OC中,消息与方法的真正实现是在执行阶段绑定的。

消息转发

编译器会将消息转发成对objc_msgSend方法的调用。

objc_msgSend方法含有两个必要的参数:receiver、selector,如:
[receiver message] 将被转换为objc_msgSend(receiver,selector);
objc_msgSend方法也能收到message的参数,如
objc_msgSend(receiver, selector, arg1, arg2, …);

objc_msgSend方法会做按照顺序进行以下操作,以完成动态绑定:

  1. 查找selector所指代的程序(方法的真正实现)。因为不同类对同一方法有不同的实现,所以对方法的真正实现的查找依赖于receiver的类
  2. 调用该实现,并将一系列参数传递过去
  3. 将该实现的返回值作为自己的返回值,返回之

消息传递的关键是,编译器构建每个类和对象时所采用的数据结构。每个类都包含以下两个必要的元素:

  • 一个指向父类的指针
  • 一个调度表(dispatch table)。该调度表讲啊类的selector与方法的实际内存地址关联起来。

类的底层构造如下:

struct objc_class {
 Class isa OBJC_ISA_AVAILABILITY; //isa指针   
#if !__OBJC2__ Class 
super_class OBJC2_UNAVAILABLE; // 父类 
const char *name OBJC2_UNAVAILABLE; // 类名 
long version OBJC2_UNAVAILABLE; // 类的版本信息,默认为0 
long info OBJC2_UNAVAILABLE; // 类信息
long instance_size OBJC2_UNAVAILABLE; // 类占据的内存大小 
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 成员变量链表 
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法链表 
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法缓存列表 
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 协议链表
 #endif
 } OBJC2_UNAVAILABLE;
  • 每个对象都有一个指向所属类的指针isa。通过该指针,对象可以找到它所属的类,也就找到了其全部父类。
  • 当向一个对象发送消息时,objc_msgSend方法根据对象的isa指针找到对象的类,然后在类的调度表(dispatch table)中查找selector。如果无法找到selector,objc_msgSend通过指向父类的指针找到父类,并在父类的调度表(dispatch table)中查找selector,以此类推直到NSObject类。一旦查找到selector,objc_msgSend方法根据调度表的内存地址调用该实现。 通过这种方式,message与方法的真正实现在执行阶段才绑定。
  • 为了保证消息发送与执行的效率,系统会将全部selector和使用过的方法的内存地址缓存起来。每个类都有一个独立的缓存,缓存包含有当前类自己的 selector以及继承自父类的selector。查找调度表(dispatch table)前,消息发送系统首先检查receiver对象的缓存。

动态方法决议与消息转发

objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行。如果在层层的寻找中,均未找到方法的实现,就是会抛出unrecognized selector sent to XXX的异常,导致程序崩溃。

但是,在crash之前,OC的运行时系统会先经过以下两个步骤:

1、Dynamic Method Resolution(动态方法决议)

首先,如果调用的方法是实例方法,OC的运行时会调用+ (BOOL)resolveInstanceMethod:(SEL)sel,如果是类方法,则会调用+ (BOOL)resolveClassMethod:(SEL)sel 让我们可以在程序运行时动态的为一个selector提供实现,如果我们添加了函数的实现,并返回YES,运行时系统会重启一次消息的发送过程,调用动态添加的方法。例如,下面的例子:

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSLog(@"未实现实例方法: %@",NSStringFromSelector(sel));
    if (sel == @selector(testMessage)) {
        class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "V@:");
    }
    return [super resolveInstanceMethod:sel];
}

void dynamicMethodIMP(id target, SEL sel) {
    NSLog(@"动态添加实例方法 %s", __PRETTY_FUNCTION__);
}

class_addMethod 方法动态的添加新的方法与对应的实现,如果调用了[test testMessage],将会转到动态添加的dynamicMethodIMP 方法中。Objective-C的方法本质上是一个至少包含两个参数(id self, SEL _cmd)的C函数,这样,当重启消息发送时,就能在类中找到@selector(testMessage)了。而如果方法返回NO时,将会进入下一步:消息转发(Message Forwarding)

2、Message Forwarding(消息转发)

消息转发分为两步:

  • 首先运行时系统会调用- (id)forwardingTargetForSelector:(SEL)aSelector方法,如果这个方法中返回的不是nil或者self,运行时系统将把消息发送给返回的那个对象
  • 如果- (id)forwardingTargetForSelector:(SEL)aSelector返回的是nil或者self,运行时系统首先会调用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法来获得方法签名,方法签名记录了方法的参数和返回值的信息,如果-methodSignatureForSelector 返回的是nil, 运行时系统会抛出unrecognized selector exception,程序到这里就结束了

实现多播代理

#import <Foundation/Foundation.h>

@interface MutipleDelegate : NSObject
/** 不增加这个对象的引用计数 */
@property (nonatomic, strong) NSPointerArray *weakRefTagets;
/** 添加一个代理对象 */
- (void)addDelegate:(id)delegate;

@end

#import "MutipleDelegate.h"

@implementation MutipleDelegate

- (void)addDelegate:(id)delegate {
    [self.weakRefTagets addPointer:(__bridge void * _Nullable)(delegate)];
}

- (NSPointerArray *)weakRefTagets {
    if (_weakRefTagets == nil) {
        _weakRefTagets = [NSPointerArray weakObjectsPointerArray];
    }
    return _weakRefTagets;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature *sign = [super methodSignatureForSelector:aSelector];
    if (sign == nil) {
        for (id target in self.weakRefTagets) {
            if ((sign = [target methodSignatureForSelector:aSelector])) {
                break ;
            }
        }
    }
    return sign;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    for (id target in self.weakRefTagets) {
        if ([target respondsToSelector:anInvocation.selector]) {
            [anInvocation invokeWithTarget:target];
        }
    }
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    if ([super respondsToSelector:aSelector]) {
        return YES;
    }

    for (id target in self.weakRefTagets) {
        if ([target respondsToSelector:aSelector]) {
            return YES;
        }
    }
    return NO;
}

@end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值