iOS消息转发学习笔记

如果深入学习ios Runtime,不得不提到消息转发,很多框架的实现都基于这一功能实现(例如JSPatch)

虽然看了很多篇关于消息转发的文章,但是理解的不是很透彻,还是自己实践一些理解能更加透彻一下。

首先我自己定义了一个MyString继承NSString

@interface MyString : NSString

@end

@implementation MyString

@end

然后创建一个MyString,通过performSelector调用MissMethod,MissMethod1,MissMethod2等方法。

- (void)testForward
{
    MyString *str = [[MyString alloc] init];
    [str performSelector:@selector(MissMethod) withObject:nil];
    [str performSelector:@selector(MissMethod2) withObject:nil];
    [str performSelector:@selector(MissMethod3) withObject:nil];
}

如果什么都不写,这样肯定会crash,会出现这个错误[NSObject(NSObject) doesNotRecognizeSelector:],因为MyString没有这三个方法,而且父类也没有。

如果没有找到方法,系统会尝试进行补救,看看有没有能处理的能力,首先会调用resolveInstanceMethod这个方法,这个方法默认是返回NO,走一下步流程,如果返回YES,例如下面方法的实现,如果传入的sel的名称是MissMethod开头,则认为我们的类是可以处理这个方法的。而且如果是MissMethod方法,我们就给这个类添加一个方法dynamicMethodIMP,作为MissMethod的实现。此时当外界调用MissMethod时,其实相当于调用dynamicMethodIMP

  void dynamicMethodIMP(id self, SEL _cmd) {

      NSLog(@" >> dynamicMethodIMP");

  }

 

+ (BOOL)resolveInstanceMethod:(SEL)name
{
    NSLog(@" >> Instance resolving %@", NSStringFromSelector(name));
    
    NSString *selName = NSStringFromSelector(name);
    
    if ([selName hasPrefix:@"MissMethod"]) {
        if (name == @selector(MissMethod)) {
            class_addMethod([self class], name, (IMP)dynamicMethodIMP, "v@:");
            return YES;
        } else {
            return NO;
        }
    }
    
    return [super resolveInstanceMethod:name];
}

如果resolveInstanceMethod方法返回NO了,下面怎么办?首先我定义了一个类MyString2作为接盘侠,实现了MissMethod1,MissMethod2方法

@interface MyString2 : NSString

@end

@implementation MyString2

- (void)MissMethod2
{
    NSLog(@"MissMethod2");
}

- (void)MissMethod3
{
    NSLog(@"MissMethod3");
}

@end

当resolveInstanceMethod方法返回NO了,系统会尝试调用下面方法,看看有没有接盘侠来接这个锅,_str2是MyString2的一个示例,而且实现了MissMethod2方法。当MyString的示例调用MissMethod2方法,MissMethod2->resolveInstanceMethod(NO)->forwardingTargetForSelector,返回一个接盘侠去给这个示例发送消息objc_sendmsg(_str2,sel)

- (id)forwardingTargetForSelector:(SEL)sel {
    if(sel == @selector(MissMethod2)){
        return _str2;
    }
    return [super forwardingTargetForSelector:sel];
}

当forwardingTargetForSelector也无法处理,返回nil时,下面会走到这个方法methodSignatureForSelector,判断sig是否为nil,如果不为nil会走forwardInvocation,最后调用forwardInvocation,如果这个方法也没有处理,做了最后尝试之后也就会抛出那个异常doesNotRecognizeSelector

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    
    NSMethodSignature *sig;
    sig = [_str2 methodSignatureForSelector:sel];
    if (sig) {
        return sig;
    }
    return [super methodSignatureForSelector:sel];
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    id target = nil;
    if ([_str2 methodSignatureForSelector:[invocation selector]] ) {
        target = _str2;
        [invocation invokeWithTarget:target];
    }
}

总结一下消息转发的整个流程,大致流程MissMethod->resolveInstanceMethod(NO)->forwardingTargetForSelector(nil)->methodSignatureForSelector(sig)->forwardInvocation。

下面是完整代码实现:

//
//  MyString.m
//  objc
//
//  Created by zilong.li on 2017/8/17.
//
//

#import "MyString.h"

#import <objc/runtime.h>

@interface MyString2 : NSString

@end

@implementation MyString2

- (void)MissMethod2
{
    NSLog(@"MissMethod2");
}

- (void)MissMethod3
{
    NSLog(@"MissMethod3");
}

@end

void dynamicMethodIMP(id self, SEL _cmd) {
    NSLog(@" >> dynamicMethodIMP");
}

@interface MyString ()
{
    MyString2 *_str2;
}

@end

@implementation MyString

- (instancetype)init
{
    self = [super init];
    if (self) {
        _str2 = [[MyString2 alloc] init];
    }
    return self;
}

+ (BOOL)resolveInstanceMethod:(SEL)name
{
    NSLog(@" >> Instance resolving %@", NSStringFromSelector(name));
    
    NSString *selName = NSStringFromSelector(name);
    
    if ([selName hasPrefix:@"MissMethod"]) {
        if (name == @selector(MissMethod)) {
            class_addMethod([self class], name, (IMP)dynamicMethodIMP, "v@:");
            return YES;
        } else {
            return NO;
        }
    }
    
    return [super resolveInstanceMethod:name];
}

- (id)forwardingTargetForSelector:(SEL)sel {
    if(sel == @selector(MissMethod2)){
        return _str2;
    }
    return [super forwardingTargetForSelector:sel];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    
    NSMethodSignature *sig;
    sig = [_str2 methodSignatureForSelector:sel];
    if (sig) {
        return sig;
    }
    return [super methodSignatureForSelector:sel];
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    id target = nil;
    if ([_str2 methodSignatureForSelector:[invocation selector]] ) {
        target = _str2;
        [invocation invokeWithTarget:target];
    }
}

@end

 

posted @ 2017-08-18 12:01  shuffle  阅读(243)  评论(0编辑  收藏  举报