OC消息转发机制

    xiaoxiao2021-12-14  18

    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方法会做按照顺序进行以下操作,以完成动态绑定:

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

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

    一个指向父类的指针一个调度表(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
    转载请注明原文地址: https://ju.6miu.com/read-964129.html

    最新回复(0)