美文网首页
oc 消息机制试用

oc 消息机制试用

作者: nunca | 来源:发表于2018-03-14 15:04 被阅读0次

由于Objective-C是动态语言,在代码中如果是通过[object method] 形式实现向object发送method消息,如果object无法响应该消息,则编译器会报错,但如果通过[object performSelector:@selector(method)]这类方式,编译器就不会做拦截,只有等到运行时才能确定object是否能响应此消息,若是发现无法响应则会导致程序崩溃,一般抛出(unrecognized selector sent to instance)错误。

- (void)viewDidLoad {
    [super viewDidLoad];
    [self performSelector:@selector(testMethod)];
}
2018-03-14 09:50:33.535715+0800 Nunca[7238:216397] -[NcTestThreeVCtr testMethod]: unrecognized selector sent to instance 0x7feaf3c2fb30
2018-03-14 09:52:27.793877+0800 Nunca[7238:216397] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NcTestVCtr testMethod]: unrecognized selector sent to instance 0x7feaf3c2fb30'

testMethod并未在类中实现,所以程序崩溃。

下面试图通过runtime中消息机制来对对象无法响应的消息进行动态绑定或转发,在程序调用doesNotRecognizeSelector方法前进行拦截。

一、.动态绑定
1.通过resolveInstanceMethod:(一般用于实现@dynamic属性)

- (void)viewDidLoad {
    [super viewDidLoad];
    [self performSelector:@selector(testMethod)];
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (![self respondsToSelector:sel]  ) {
        class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

void dynamicMethodIMP (id self, SEL _cmd) {
    NSLog(@"%@----", NSStringFromSelector(_cmd));
}
2018-03-14 09:52:36.409159+0800 Nunca[883:10041] testMethod----

testMethod并未在类中实现,但可以通过动态绑定为此方法绑定实现地址dynamicMethodIMP(IMP是一个函数指针,指向对应方法的实现部分),类中所有未实现方法的调用都可以通过此方法进行拦截,防止崩溃。

二、消息转发
1.通过forwardingTargetForSelector (可实现多重继承)

@interface NcTestClass ()
@end

@implementation NcTestClass
- (void)testMethod {
    NSLog(@"testMethod in NcTestClass----");
}
@end

@interface NcTestVCtr ()
@end

@implementation NcTestVCtr
- (void)viewDidLoad {
    [super viewDidLoad];
    [self performSelector:@selector(testMethod)];
}

- (id)forwardingTargetForSelector:(SEL)aSelector  {
    if ([NSStringFromSelector(aSelector) isEqualToString:@"testMethod"] ) {
        NSLog(@"forwarding selector to NcTestClass----");
        return [[NcTestClass alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}
@end

2018-03-14 10:52:33.267644+0800 Nunca[2400:43738] forwarding selector to NcTestClass----
2018-03-14 10:52:33.268506+0800 Nunca[2400:43738] testMethod in NcTestClass----

testMethod并未在NcTestVCtr中实现,NcTestVCtr通过forwardingTargetForSelector方法将testMethod转发至NcTestClass,最终NcTestClass中的testMethod被执行
forwardingTargetForSelector无法操作转发过程中的参数与返回值

2.通过forwardInvocation: (完整消息转发)
在调用forwardInvocation: 方法之前系统会先调用methodSignatureForSelector:方法,若methodSignatureForSelector: 返回值为nil 则会触发doesNotRecognizeSelector抛出异常然后crash,只有当返回签名不为空才会继续调用forwardInvocation: 进行消息转发

@interface NcTestClass ()
@end

@implementation NcTestClass
- (void)testMethod {
    NSLog(@"testMethod in NcTestClass----");
}
@end

@interface NcTestThreeVCtr ()
@end

@implementation NcTestThreeVCtr

- (void)viewDidLoad {
    [super viewDidLoad];
    [self performSelector:@selector(testMethod)];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
    if (!signature) {
        if ([NcTestClass instancesRespondToSelector:aSelector]) {
            signature = [NcTestClass instanceMethodSignatureForSelector:aSelector];
        }
    }
    return signature;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"forwardInvocation ----");
}
2018-03-14 14:17:56.303144+0800 Nunca[6479:168308] forwardInvocation ----

此时程序不崩溃(若forwardInvocation:方法未在类中实现则会导致崩溃),但由于只进行了签名 并未进行转发,所以NcTestClass中的testMethod也不会被执行

修改forwardInvocation:中的代码:

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"forwardInvocation ----");
    if ([NcTestClass instancesRespondToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:[[NcTestClass alloc] init]];
    }
}

打印运行结果:

2018-03-14 14:25:50.562469+0800 Nunca[6606:175681] forwardInvocation ----
2018-03-14 14:25:50.562643+0800 Nunca[6606:175681] testMethod in NcTestClass----

此时消息转发成功

以上都是针对实例方法的消息绑定与转发,针对类方法也有相对应的绑定与转发方法,大体相同,就不一一罗列了。

相关文章

  • oc 消息机制试用

    由于Objective-C是动态语言,在代码中如果是通过[object method] 形式实现向object发送...

  • iOS runtime

    runtime 是 oc 语音的基础首先runtime的核心机制是消息机制 也就是oc的消息机制首先oc的消息机制...

  • OC中的消息机制和动态运行时

    消息机制:OC中的实例对象调用一个方法称作消息传递 OC中里的消息传递采用动态绑定机制来决定具体调用哪个方法,OC...

  • OC消息机制

    基于Runtime的动态特性 在苹果的官方文档中,对Runtime的介绍如下: The Objective-C l...

  • oc消息机制

    内存分区 1栈 局部变量 2堆alloc分配地址 在方法中(函数体)定义的变量通常是在栈内,因此如果你的变量要跨函...

  • OC消息机制

    1.消息机制RunTime简称运行时。就是系统在运行的时候的一些机制,其中最主要的是消息机制。对于C语言,函数的调...

  • OC消息机制,消息转发机制

    Runtime简称运行时,其中最主要的是消息机制 概述 C 与 OC 的不同 1.C 语言,函数的调用在编译的时候...

  • 关于Runtime 消息发送机制的延伸

    说到OC 不得不说一下OC 的消息转发机制;何为OC 的消息转发机制;其实就是这样的; Objc 在向一个对象发送...

  • Runtime知识点整理1

    OC消息机制?消息转发机制流程?什么是Runtime?什么场景下使用? ==============巴拉巴拉......

  • Runtime部分

    --------------------Runtime-------------------- OC的消息机制 O...

网友评论

      本文标题:oc 消息机制试用

      本文链接:https://www.haomeiwen.com/subject/hnwxqftx.html