Runtime--Dynamic Method Resolution
很多时候你想动态的提供方法的实现,比如说声明property的时候使用编译器指令@dynamic
@dynamic propertyName;
可以通过实现以下两个方法达到动态实现方法的目的
//如果找到方法实现并且添加到Class,则返回Yes,否则NO
//针对类方法
+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//如果找到方法实现并且添加到Class,则返回Yes,否则NO
//针对实例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
为了模拟场景,首先在新建工程的ViewController中声明但是不实现两个方法
@interface ViewController ()
-(void)ivarMethod;
+(void)classMethod;
@end
//此处会收到警告提示方法未实现
@implementation ViewController
然后定义两个对应的函数,Objective-C方法简单来说就是至少带有两个参数的C函数,这两个参数为self
and _cmd
,代码如下
void ivar(id self,SEL _cmd)
{
NSLog(@"调用ivar");
}
void class(id self,SEL _cmd)
{
NSLog(@"调用class");
}
然后利用Runtime的class_addMethod
函数给方法添加对应的implementation
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(ivarMethod))
{
bool success = class_addMethod([self class], sel, (IMP)ivar, "v@:");
return success;
}
return [super resolveInstanceMethod:sel];
}
+ (BOOL)resolveClassMethod:(SEL)sel
{
if (sel == @selector(classMethod))
{
//此二者均可:object_getClass(self)、objc_getMetaClass("ViewController")
bool success = class_addMethod(object_getClass(self), sel, (IMP)class, "v@:");
return success;
}
return [super resolveClassMethod:sel];
}
最好调用方法完成动态添加实现
- (void)viewDidLoad
{
[super viewDidLoad];
[self ivarMethod];
[ViewController classMethod];
}
//输出结果
调用ivar
调用class
看到输出结果表示动态添加效果已实现。
Message Forwarding 和 Dynamic method resolution
后者发生在前者之前。如果已经实现 resolveInstanceMethod:
又想进行消息转发,那么这些方法都要返回NO(不要给方法添加实现)。下面通过代码来验证一下。
首先在以上代码的基础上重写消息转发必要的两个方法
//Data类中方法实现
-(void)ivarMethod
{
NSLog(@"调用ivarMethod");
}
/***********分割线***********/
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSString *sel = NSStringFromSelector(anInvocation.selector);
if ([sel isEqualToString:@"ivarMethod"])
{
//消息转发到Data中
[anInvocation invokeWithTarget:[[Data alloc] init]];
}
else
{
[super forwardInvocation:anInvocation];
}
}
-(NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature *signature = [super methodSignatureForSelector:selector];
//生成方法签名
if (! signature)
{
//处理不同的方法
if (sel_isEqual(selector, @selector(ivarMethod)))
{
signature = [[[Data alloc] init] methodSignatureForSelector:selector];
}
}
return signature;
}
然后resolveInstanceMethod:没有添加动态实现才能进行消息转发,否则直接执行了
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSLog(@"resolveInstanceMethod");
return [super resolveInstanceMethod:sel];
}
调用方法,通过打印结果顺序验证结果
- (void)viewDidLoad {
[super viewDidLoad];
[self ivarMethod];
}
//输出结果
resolveInstanceMethod
调用ivarMethod
forwardingTargetForSelector:
- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
1、当有unrecognized消息时,返回一个对象,以便消息可以转发。
2、如果一个对象继承或者实现了此方法,而且返回此对象,那么消息被成功转发调用
3、禁止返回self对象,否则引起死循环
4、对于不处理的方法,返回super’s implementation的结果
5、调用发生在forwardInvocation:
之前;转发速度更快
6、不能够像NSInvocation那样操作返回值和参数
下边通过代码展示简单用法和验证执行顺序,在以上代码的基础上
- (id)forwardingTargetForSelector:(SEL)aSelector
{
NSLog(@"forwardingTargetForSelector");
if (aSelector == @selector(ivarMethod))
{
return [[Data alloc] init];
}
else
{
return [super forwardingTargetForSelector:aSelector];
}
}
/***********分割线***********/
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSString *sel = NSStringFromSelector(anInvocation.selector);
//方法对比打印次序
NSLog(@"forwardInvocation");
if ([sel isEqualToString:@"ivarMethod"])
{
[anInvocation invokeWithTarget:[[Data alloc] init]];
}
else
{
[super forwardInvocation:anInvocation];
}
}
调用变查看输出结果
- (void)viewDidLoad {
[super viewDidLoad];
[self ivarMethod];
}
//输出结果
resolveInstanceMethod //Dynamic Method Resolution机制
forwardingTargetForSelector //消息转发
调用ivarMethod //方法调用
通过结果可以看出处理 unrecognized messages的顺序:
1、Dynamic Method Resolution机制
2、- (void)forwardInvocation:(NSInvocation *)anInvocation
3、Message Forwarding
参考文献:Dynamic Method Resolution、NSObject、forwardingTargetForSelector:、Message Forwarding