runtime动态添加方法引出的消息转发

来源:互联网 发布:php打印空格 编辑:程序博客网 时间:2024/05/18 19:35

首先甩出官方文档链接:
https://developer.apple.com/reference/objectivec/1418901-class_addmethod?language=objc


运行时开放的接口

BOOL class_addMethod(Class cls, SEL name, IMP imp,                                  const char *types) 
  • 一:四个参数的意义:

1:cls
添加类方法到哪个类上面去
2:name
一个选择器,用于指定方法的名称,即:方法的名称
3:imp
新方法的实现函数,这个函数必须至少有两个参数 self 和 _cmd (根据运行时官方文档的解释:An Objective-C method is simply a C function that take at least two arguments—self and _cmd. For example, given the following function:
void myMethodIMP(id self, SEL _cmd)
{
// implementation ....
}

一个OC的方法,其实就是一个至少有self 和 _cmd两个参数的C函数)
4:types
一个字符的集合,用来描述这个方法的参数类型,其他可能的值,请参阅
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100其中第二个和第三个字符必须为”@:”.而第一个参数是要添加的函数的返回值类型.

  • 二:返回值的意义

如果返回YES.则动态添加成功,如果是NO,则添加不成功(例如:当前类已经包含了一个同样名称方法的实现了);

  • 三:按照苹果给出的动态添加类方法的例子如下

:

class_addMethod([self class], @selector(resolveThisMethodDynamically), (IMP) myMethodIMP, "v@:");

其中. resolveThisMethodDynamically就是在运行时.调用的当前类的并没有实现和声明的方法,而myMethodIMP就是一个C的函数,调用了resolveThisMethodDynamically方法后,执行的函数就是myMethodIMP


  • 四:实例代码
    为People动态添加一个方法
#import "People.h"#import <objc/runtime.h>int myMethodIMP(id self, SEL _cmd,NSString *str){    NSLog(@"动态添加的方法做的事 %@",str);    return 100;}@implementation People+ (BOOL)resolveInstanceMethod:(SEL)sel{    SEL method = NSSelectorFromString(@"resolveThisMethodDynamically");    if (sel == method) {        class_addMethod([self class], method, (IMP)myMethodIMP, "i@:");        return YES;    }    return [super resolveInstanceMethod:sel];}@end

在viewDidLoad里面调用

- (void)viewDidLoad {    [super viewDidLoad];    People *P = [[People alloc]init];    [P performSelector:@selector(resolveThisMethodDynamically) withObject:@"lausen"];}

可以看到.上述方法中用到了+ (BOOL)resolveInstanceMethod:(SEL)sel这个方法,涉及到了OC中消息的转发.


  • 五:消息的转发
    OC中,对象调用方法是一个给对象发送消息的过程,因为OC采用的是”动态绑定机制”,要调用的方法,知道运行时才能确定。

如果对象不能接受这个消息,就会调用以下这几个方法,给你进行”消息转发”的机会,我们就是利用这几个方法进行消息转发,需要注意的是,按顺序执行以下方法,如果前面的已经实现了,并且执行了,就不会执行下面的方法。如果都没执行,那么程序就会报错

第一个:

+ (BOOL)resolveClassMethod:(SEL)sel + (BOOL)resolveInstanceMethod:(SEL)sel

第二个:

- (id)forwardingTargetForSelector:(SEL)aSelector

第三个:

-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector- (void)forwardInvocation:(NSInvocation *)anInvocation

三个方法的调用顺序和前后关系,如下图所示,(盗图:http://blog.csdn.net/li198847/article/details/51375635)
这里写图片描述

第一个方法:如果返回YES.则表示可以处理消息,如果处理不了,则调用第二个方法(其实第一个方法,就是看一下如果当前的类/对象,处理不了这个消息,那就看一下,当前的类里面有没有通过运行时去动态添加方法,来进行消息转发)
第二个方法:通过该方法.返回一个可以处理该消息的对象,
例如:新建一个Animal类和Dog类,均继承自NSObject
在Animal类里面声明一个和Dog一样的一个方法
Animal.h

#import <Foundation/Foundation.h>@interface Animal : NSObject- (void)run:(NSString *)age;@end

Animal.m里面实现

#import "Animal.h"#import "Dog.h"@implementation Animal- (id)forwardingTargetForSelector:(SEL)aSelector{    NSString *selStr = NSStringFromSelector(aSelector);    if ([selStr isEqualToString:@"run:"]) {        return [[Dog alloc] init];    }    return [super forwardingTargetForSelector:aSelector];}@end

Dog.h

#import <Foundation/Foundation.h>@interface Dog : NSObject- (void)run:(NSString *)age;@end

Dog.m

#import "Dog.h"@implementation Dog- (void)run:(NSString *)age{    NSLog(@"%@岁的��在跑",age);}@end

用Animal调用Animal的run方法

- (void)viewDidLoad {    [super viewDidLoad];    Animal *animal = [[Animal alloc]init];    [animal run:@"2"];}

打印结果如下:
这里写图片描述

1 0
原创粉丝点击