当对象收到无法解读的消息后,就会尝试将消息转发。整体流程是这样的:
1.+ (BOOL)resolveInstanceMethod:(SEL)sel或+ (BOOL)resolveClassMethod:(SEL)sel
2.- (id)forwardingTargetForSelector:(SEL)aSelector
3.- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
4.- (void)forwardInvocation:(NSInvocation *)invocation
@interface Man : NSObject
- (void)doSomething;
@end
@implementation Man
- (void)doSomething
{
NSLog(@"doSomething in Class Man");
}
@end
@interface Person : NSObject
@end
@interface Person ()
@property (nonatomic,strong)Man *m;
@end
@implementation Person
- (instancetype)init
{
if (self = [super init])
{
self.m = [[Man alloc] init];
}
return self;
}
void MethodIMP(id self,SEL _cmd)
{
NSLog(@"doSomething at resolveInstanceMethod:");
}
// 对象收到无法处理的消息后,首先将调用类方法:+ (BOOL)resolveInstanceMethod:(SEL)sel
// 该方法的参数就是那个未知的选择子,其返回值为bool类型,表示这个类是否能新增一个实例方法用以处理此选择子。
// 在继续往下执行转发机制之前,有机会新增一个处理此选择子的方法。
// 假如尚未实现的方法是类方法,那么会调用另外一个方法
// + (BOOL)resolveClassMethod:(SEL)sel
// 此方案常用来实现@dynamic属性。
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
BOOL res = [super resolveInstanceMethod:sel];
if (sel == @selector(doSomething))
{
NSLog(@"add method at resolveInstanceMethod:");
// OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
// types 定义该数返回值类型和参数类型的字符串,这里比如"v@:",其中v就是void,带表返回类型就是空,@代表参数,这里指的 是id(self),这里:指的是方法SEL(_cmd),比如:
// int method(id self, SEL _cmd, NSString *string)
// 那么添加这个函数的方法就应该是ass_addMethod([self class], @selector(newMethod), (IMP)newMethod, "i@:@");
class_addMethod([self class], sel, (IMP)MethodIMP, "v@:");
res = YES;
}
return res;
}
/*
当前接收者还有第二次机会进行处理.
- (id)forwardingTargetForSelector:(SEL)aSelector
方法参数代表未知的选择子,若当前接收者能找到被援对象,则将其返回,若找不到就返回nil。在一个对象内部,可能还有一系列其他对象,该对象可经由此方法将能够处理某选择子的相关内部对象返回。
*/
- (id)forwardingTargetForSelector:(SEL)aSelector
{
id recv = nil;
if (aSelector == @selector(doSomething))
{
recv = self.m;
}
return recv;
}
/*
这一步是最后一次机会。首先调用
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
消息获得函数的参数和返回值类型。如果返回nil,则会发出- (void)doesNotRecognizeSelector:(SEL)aSelector,
这时也就挂掉了。如果返回了一个函数签名,就会创建一个NSInvocation对象并发送
- (void)forwardInvocation:(NSInvocation *)invocation给目标对象。
*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature *res = [super methodSignatureForSelector:aSelector];
if (aSelector == @selector(doSomething))
{
res = [self.m methodSignatureForSelector:aSelector];
}
return res;
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
if([self.m respondsToSelector:invocation.selector])
{
[invocation invokeWithTarget:self.m];
}
else
{
[self doesNotRecognizeSelector:invocation.selector];
}
}
@end
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Person *person = [[Person alloc] init];
[person performSelector:@selector(doSomething)];
}
return 0;
}
复制代码