稍微理解一些 Objective-C 的同学都知道, OC 下面调用一个函数, 如[obj somemethod] 其实是一个消息发送的过程, 使用 objc_msgSend(obj, selector) 给对象发送, 当然, 最后还是会设计到找到具体函数执行的过程.
如果你去对某个对象调用一个没有实现的方法, 会报 unrecognized selector
异常. 但是在此之前, 有几次挽救的机会, 我们来看一下这段代码
//
// main.m
// MessageSendDemo
//
// Copyright © 2017年 yww. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface MyObject2 : NSObject
- (void) func3;
- (void) func4;
@end
@implementation MyObject2
- (void) func3 {
NSLog(@"func3");
}
- (void) func4 {
NSLog(@"func4");
}
@end
void func2(id self, SEL _cmd) {
NSLog(@"func2");
}
@interface MyObject : NSObject
- (void) func1;
@end
@implementation MyObject
- (void) func1 {
NSLog(@"func1");
}
// 第一次尝试
+ (BOOL) resolveInstanceMethod:(SEL)sel {
if(sel == @selector(func2)) {
class_addMethod(self, @selector(func2), (IMP)func2, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
// 第二次尝试
- (id) forwardingTargetForSelector:(SEL)aSelector {
if(aSelector == @selector(func3)) {
return [MyObject2 new];
}
return [super forwardingTargetForSelector:aSelector];
}
// 第三次尝试
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if(aSelector == @selector(func4)) {
return [[MyObject2 new] methodSignatureForSelector:aSelector];
}
return [super methodSignatureForSelector:aSelector];
}
-(void)forwardInvocation:(NSInvocation *)anInvocation {
if(anInvocation.selector == @selector(func4)) {
[anInvocation invokeWithTarget:[MyObject2 new]];
}
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
[[MyObject new] func1];
[[MyObject new] performSelector:@selector(func2)];
[[MyObject new] performSelector:@selector(func3)];
[[MyObject new] performSelector:@selector(func4)];
}
return 0;
}
我们定义了一个类, MyObject, 里面只有一个 func1 方法. 正常情况下,[[MyObject new] performSelector:@selector(func2)];
, [[MyObject new] performSelector:@selector(func3)];
, [[MyObject new] performSelector:@selector(func4)];
这三句都应该会发生unrecognized selector
异常. 可是我们这里对这三个方法进行了处理.
对于 func2, 我们实现了+ resolveInstanceMethod:
方法, 并在这个方法中, 动态增加了一个实例方法func2, 然后返回 YES, 重启消息转发, 最终成功调用到 func2.
对于 func3, 使用了快速转发, 我们实现了-forwardingTargetForSelector:
将 func3 转发给了其他对象处理.
最后的 func4 则是使用了普通转发, 需要实现 -methodSignatureForSelector:
和 -forwardInvocation
也是通过把消息转发给 其他对象来处理, 达到调用 func4 的目的
有兴趣的同学可以看我写的这个 demo https://github.com/ywwzwb/MessageSendDemo
网友评论