上一篇说了很多objc_msgSend构成一类的东西,这篇说些别的。
废话不多说看下面代码。
int a(id self, SEL _cmd) {
return 1;
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
class_addMethod([self class], @selector(a), (IMP)a, "i@:");
return YES;
}
@end
int main(int argc, char * argv[]) {
A *aObject = [[A alloc] init];
NSLog(@"%ld", aObject.a);
}
void b(id self, SEL _cmd) {
NSLog(@"b");
}
+ (BOOL)resolveClassMethod:(SEL)sel {
class_addMethod([self class], @selector(b), (IMP)b, "v@:");
return YES;
}
@end
int main(int argc, char * argv[]) {
[[A class] performSelector:@selector(b)]; // 因为[A b];这种调用方式会编译错误,所以动态调用
}
怎么说?明白要说什么吗?动态绑定方法,第一种绑定的.a这个实例方法,第二种,应该也可以看出来是绑定的类方法。那么?
第二种真的可以绑定成功吗?答案是错的。
+ (BOOL)resolveClassMethod:(SEL)sel {
Class aMeta = objc_getMetaClass(class_getName([self class]));
class_addMethod([aMeta class], @selector(b), (IMP)b, "v@:");
return YES;
}
这是正确的方法。为啥?
因为之前说过的,类方法存在mataclass的methodlis中,所以,必须添加到meta中。so。
不知道你有没有注意到这两个方法。
+ (BOOL)resolveInstanceMethod:(SEL)sel;
+ (BOOL)resolveClassMethod:(SEL)sel ;
当然啦,这是消息转发的环节开始喽。
下面开始讲消息转发机制。
第一步
+ (BOOL)resolveInstanceMethod:(SEL)sel;
+ (BOOL)resolveClassMethod:(SEL)sel ;
这两个方法是消息转发机制的第一步,在这一步我们可以做添加方法,添加对象等操作。如果返回NO。则进入下一步
第二步
这一步我们需要做的是,返回一个可以正确响应消息的对象。如果获取到,则直接把消息转发给它,返回非nil对象。否则返回nil,继续下面的动作。注意这里不要返回self,否则会形成死循环。
- (id)forwardingTargetForSelector:(SEL)aSelector
- (id)forwardingTargetForSelector:(SEL)aSelector
{
NSString *seletorString = NSStringFromSelector(aSelector);
if ([@"myobjc" isEqualToString:seletorString]) {
Student *s = [[Student alloc] init];
return s;
}
// 继续转发
return [super forwardingTargetForSelector:aSelector];
}
第三步
获得方法签名,这里你有机会进一步挽救程序崩溃。
尝试获得一个方法签名。如果获取不到,则直接调用doesNotRecognizeSelector抛出异常。如果能获取,则返回非nil;传给一个NSInvocation并传给forwardInvocation:。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
举几个具体的例子
第一个
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature* signature = [super methodSignatureForSelector:aSelector];
if (!signature) {
B *bObject = [[B alloc] init];
signature = [bObject methodSignatureForSelector:aSelector];
}
return signature;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
B *bObject = [[B alloc] init];
[anInvocation invokeWithTarget:bObject];
}
第二个,这个例子,可以说讲述了消息转发第三步和第四步的所有过程
// 完整的消息转发
- (void)travel:(NSString*)city
{
NSLog(@"Teacher travel:%@", city);
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSString *method = NSStringFromSelector(aSelector);
if ([@"playPiano" isEqualToString:method]) {
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"];
return signature;
}
return nil;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
SEL sel = @selector(travel:);
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"];
anInvocation = [NSInvocation invocationWithMethodSignature:signature];
[anInvocation setTarget:self];
[anInvocation setSelector:@selector(travel:)];
NSString *city = @"北京";
// 消息的第一个参数是self,第二个参数是选择子,所以"北京"是第三个参数
[anInvocation setArgument:&city atIndex:2];
if ([self respondsToSelector:sel]) {
[anInvocation invokeWithTarget:self];
return;
} else {
Student *s = [[Student alloc] init];
if ([s respondsToSelector:sel]) {
[anInvocation invokeWithTarget:s];
return;
}
}
// 从继承树中查找
[super forwardInvocation:anInvocation];
}
第四步
将第三步获取到的方法签名包装成Invocation传入,如何处理就在这里面了,并返回非nil。
- (void)forwardInvocation:(NSInvocation *)anInvocation
第五步 没有找到合适的接受者。崩溃
- (void)doesNotRecognizeSelector:(SEL)aSelector {
[super doesNotRecognizeSelector:aSelector];
}