objective-c 消息动态解析与转发

2021-10-13  本文已影响0人  yxibng

参考:Objective-C Runtime Programming Guide
demo 链接

向一个oc对象发送一个不存在的消息,会抛出 unrecognized selector sent to instance xxx 的异常,导致程序终止。
在抛出异常之前,用户有机会通过动态方法解析或者消息转发来阻止异常的发生


image.png

动态方法解析

/*
动态解析
1. 可以解析实例方法
2. 可以解析类方法
*/
Resolver *resolver = [[Resolver alloc] init];
[resolver doSomething];
[Resolver doSomething];

Resolver

@interface Resolver : NSObject
- (void)doSomething;
+ (void)doSomething;
@end

@implementation Resolver
void instanceMethodIMP(id self, SEL _cmd) {
    NSLog(@"%s",__FUNCTION__);
}

void classMethodIMP(id self, SEL _cmd) {
    NSLog(@"%s",__FUNCTION__);
}

+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(doSomething)) {
        Class meta = object_getClass([self class]);
        class_addMethod(meta, @selector(doSomething), (IMP)classMethodIMP, "v@:");
        return YES;
    }
    return [super resolveClassMethod:sel];
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(doSomething)) {
        class_addMethod([self class], @selector(doSomething), (IMP)instanceMethodIMP, "v@:");
        return YES;
    }
    return [super resolveClassMethod:sel];
}
@end

转发给其他对象

/*
    快速转发,将消息转发给别的可以响应的对象
    */
Receiver *receiver = [[Receiver alloc] init];
[receiver doSomething];

Receiver

@interface Receiver : NSObject
- (void)doSomething;
@end

@interface Receiver ()
@property (nonatomic, strong) Handler *handler;
@end

@implementation Receiver

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

- (id)forwardingTargetForSelector:(SEL)aSelector {
    
    if (aSelector == @selector(doSomething)) {
        if ([_handler respondsToSelector:aSelector]) {
            return _handler;
        }
    }
    return [super forwardingTargetForSelector:aSelector];
    
}
@end

Handler

@interface Handler : NSObject
- (void)doSomething;
@end

@implementation Handler
- (void)doSomething {
    NSLog(@"%s",__FUNCTION__);
}
@end

NSInvocation转发

/*
    对消息重新签名,通过invocation来转发
    1. 可以修改参数,返回值,接受对象
    2. 将消息转发给多个对象
    3. 不处理的话,消息就被吞掉了
    */
AReceiver *aReceiver = [[AReceiver alloc] init];
[aReceiver doSomething];

BOOL ret = [aReceiver doSomethingWithParam1:@"param1" param2:"param2"];
NSLog(@"ret = %d",ret);

AReceiver

@interface AReceiver : NSObject
- (void)doSomething;
- (BOOL)doSomethingWithParam1:(NSString *)param1 param2:(char *)param2;
@end

@interface AHandler : NSObject

@end

@implementation AHandler
- (void)doSomething {
    NSLog(@"%s",__FUNCTION__);
}
- (BOOL)doSomethingWithParam1:(NSString *)param1 param2:(char *)param2 {
    NSLog(@"%s, parma1 = %@, param2 = %s",__FUNCTION__, param1, param2);
    return YES;
}

- (void)doWithParam1:(NSString *)param1 param2:(char *)param2 {
    NSLog(@"%s, parma1 = %@, param2 = %s",__FUNCTION__, param1, param2);
}



@end


@implementation AReceiver

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(doSomething)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    
    if (aSelector == @selector(doSomethingWithParam1:param2:)) {
        return [NSMethodSignature signatureWithObjCTypes:"c@:@*"];
    }
    
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    
    //将消息转发给其他对象
    if (anInvocation.selector == @selector(doSomething)) {
        [anInvocation invokeWithTarget:[AHandler new]];
    }
    if (anInvocation.selector == @selector(doSomethingWithParam1:param2:)) {
        //篡改消息的参数
        NSString *param1 = @"message1 hacked";
        char *param2 = "message2 hacked";
        [anInvocation setArgument:&param1 atIndex:2];
        [anInvocation setArgument:&param2 atIndex:3];
        //篡改消息的方法选择器
        anInvocation.selector = @selector(doWithParam1:param2:);
        //修改消息接收对象
        [anInvocation invokeWithTarget:[AHandler new]];
        //篡改消息的返回值
        BOOL ret = YES;
        [anInvocation setReturnValue:&ret];
    }
}
@end
上一篇 下一篇

猜你喜欢

热点阅读