编写高质量代码的52个有效方法

52个有效方法(12) - 理解消息转发机制

2018-08-30  本文已影响28人  SkyMing一C
NSObject的消息转发的方法
//接受到无法解读的 类方法消息 时调用
+ (BOOL)resolveClassMethod:(SEL)sel ;


//接受到无法解读的 实例方法的消息 时调用
+ (BOOL)resolveInstanceMethod:(SEL)sel ;

//备授接受者
- (id)forwardingTargetForSelector:(SEL)aSelector;

//转发消息
- (void)forwardInvocation:(NSInvocation *)anInvocation;
//方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;

//无法找到方法时调用
- (void)doesNotRecognizeSelector:(SEL)aSelector;
消息转发全流程
消息转发
1.动态方法解析
//接受到无法解读的 类方法消息 时调用
+ (BOOL)resolveClassMethod:(SEL)sel ;

//接受到无法解读的 实例方法的消息 时调用
+ (BOOL)resolveInstanceMethod:(SEL)sel ;

2.备援接受者

倘若没有动态新增方法来响应该选择子,则该对象会检查是否有其他对象来处理这条消息,也就是执行forwardingTargetForSelector:方法寻找备授接受者。

3.完整的消息转发机制
4.抛出异常

若最终仍无法处理该消息,那么会调用NSObject的- (void)doesNotRecognizeSelector:(SEL)aSelector方法,抛出异常。

5.接收者在每一步中均有机会处理消息。步骤越往后,处理消息的代价就越大。
//
//  YYWeakProxy.m
//  YYKit <https://github.com/ibireme/YYKit>
//
//  Created by ibireme on 14/10/18.
//  Copyright (c) 2015 ibireme.
//
//  This source code is licensed under the MIT-style license found in the
//  LICENSE file in the root directory of this source tree.
//

#import "YYWeakProxy.h"


@implementation YYWeakProxy

- (instancetype)initWithTarget:(id)target {
    _target = target;
    return self;
}

+ (instancetype)proxyWithTarget:(id)target {
    return [[YYWeakProxy alloc] initWithTarget:target];
}

- (id)forwardingTargetForSelector:(SEL)selector {
    return _target;
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    void *null = NULL;
    [invocation setReturnValue:&null];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    return [_target respondsToSelector:aSelector];
}

- (BOOL)isEqual:(id)object {
    return [_target isEqual:object];
}

- (NSUInteger)hash {
    return [_target hash];
}

- (Class)superclass {
    return [_target superclass];
}

- (Class)class {
    return [_target class];
}

- (BOOL)isKindOfClass:(Class)aClass {
    return [_target isKindOfClass:aClass];
}

- (BOOL)isMemberOfClass:(Class)aClass {
    return [_target isMemberOfClass:aClass];
}

- (BOOL)conformsToProtocol:(Protocol *)aProtocol {
    return [_target conformsToProtocol:aProtocol];
}

- (BOOL)isProxy {
    return YES;
}

- (NSString *)description {
    return [_target description];
}

- (NSString *)debugDescription {
    return [_target debugDescription];
}

@end
6.NSProxy
/*  NSProxy.h
    Copyright (c) 1994-2017, Apple Inc. All rights reserved.
*/

#import <Foundation/NSObject.h>

@class NSMethodSignature, NSInvocation;

NS_ASSUME_NONNULL_BEGIN

NS_ROOT_CLASS
@interface NSProxy <NSObject> {
    Class   isa;
}

+ (id)alloc;
+ (id)allocWithZone:(nullable NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
+ (Class)class;

- (void)forwardInvocation:(NSInvocation *)invocation;
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available");
- (void)dealloc;
- (void)finalize;
@property (readonly, copy) NSString *description;
@property (readonly, copy) NSString *debugDescription;
+ (BOOL)respondsToSelector:(SEL)aSelector;

- (BOOL)allowsWeakReference NS_UNAVAILABLE;
- (BOOL)retainWeakReference NS_UNAVAILABLE;

// - (id)forwardingTargetForSelector:(SEL)aSelector;

@end

NS_ASSUME_NONNULL_END

要点
  1. 若对象无法响应某个选择子,则进入消息转发流程。

  2. 通过运行期的动态方法解析功能,我们可以在需要用到某个方法时再将其加入类中。

  3. 对象可以把其无法解读的某些选择子转交给其他对象来处理。

  4. 经过上述两步之后,如果还是没办法处理选择子,那就启动完整的消息转发机制。

上一篇 下一篇

猜你喜欢

热点阅读