Objective-C Runtime 运行时:方法中 SEL, IMP, Method 的定义与关系

SEL

  • 代表方法的名称。仅以名字来识别。
  • 不论两个类是否存在依存关系,只要他们拥有相同的方法名,那么他们的SEL都是相同的。比如,有n个viewcontroller页面,每个页面都有一个viewdidload,每个页面的载入,肯定都是不尽相同的。但是我们可以通过打印,观察发现,这些viewdidloadSEL都是同一个:
SEL sel = @selector(methodName);
NSLog(@"address = %p",sel);// log输出为 address = 0x100002d72
  • 因此类方法定义时,尽量不要用相同的名字,就算是变量类型不同也不行。否则会引起重复,例如:
-(void)setWidth:(int)width;
-(void)setWidth:(double)width;

IMP

  • 定义:
id (*IMP)(id, SEL, ...)

个人理解:

  • SEL只提供一个名字,但是至于这个名字属于谁,并不知道。A类和B类的SEL实际实现IMP是不一样的。IMP其实是implementation的缩写。
  • IMP定义中存在两个参数,id 即为指向self的指针。SEL就是上面说的方法名
  • 综合来看,IMP可以理解为,我知道有一个方法,是SEL告诉我的,但是这是哪个类的里面具体的实现方法,需要id告诉我。两者确定后,我就可以获得某个指定的方法的内容了IMP相当于是函数指针。

Method

  • 我们可以看到该结构体中包含一个SEL和IMP,实际上相当于在SEL和IMP之间作了一个映射。有了SEL,我们便可以找到对应的IMP,从而调用方法的实现代码。具体操作流程我们将在下面讨论。
  • 个人理解,Method相当于是把二者综合在一起,是一个完整的,我们传统意义上理解的方法。

Method 方法及说明

// 添加方法
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );

// 获取实例方法
Method class_getInstanceMethod ( Class cls, SEL name );

// 获取类方法
Method class_getClassMethod ( Class cls, SEL name );

// 获取所有方法的数组
Method * class_copyMethodList ( Class cls, unsigned int *outCount );

// 替代方法的实现
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );

// 返回方法的具体实现
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );

// 类实例是否响应指定的selector
BOOL class_respondsToSelector ( Class cls, SEL sel );

说明

  • class_addMethod的实现会覆盖父类的方法实现,但不会取代本类中已存在的实现,如果本类中包含一个同名的实现,则函数会返回NO。如果要修改已存在实现,可以使用method_setImplementation 一个Objective-C方法是一个简单的C函数,它至少包含两个参数–self_cmd。所以,我们的实现函数(IMP参数指向的函数)至少需要两个参数

  • class_getInstanceMethodclass_getClassMethod函数,与class_copyMethodList不同的是,这两个函数都会去搜索父类的实现。

  • class_copyMethodList函数,返回包含所有实例方法的数组,如果需要获取类方法,则可以使用class_copyMethodList(object_getClass(cls), &count)(一个类的实例方法是定义在元类里面)。该列表不包含父类实现的方法。outCount参数返回方法的个数。在获取到列表后,我们需要使用free()方法来释放它。

  • class_replaceMethod函数,该函数的行为可以分为两种:如果类中不存在name指定的方法,则类似于class_addMethod函数一样会添加方法;如果类中已存在name指定的方法,则类似于method_setImplementation一样替代原方法的实现。

  • class_getMethodImplementation函数,该函数在向类实例发送消息时会被调用,并返回一个指向方法实现函数的指针。这个函数会比method_getImplementation(class_getInstanceMethod(cls, name))更快。返回的函数指针可能是一个指向runtime内部的函数,而不一定是方法的实际实现。例如,如果类实例无法响应selector,则返回的函数指针将是运行时消息转发机制的一部分。

  • class_respondsToSelector函数,我们通常使用NSObject类的respondsToSelector:instancesRespondToSelector:方法来达到相同目的。

runtime 中 types的说明

const char *types一个定义该函数返回值类型和参数类型的字符串
举例说明

  • 函数原型
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
  • 说明
class_addMethod([TestClass class], @selector(ocMethod:), (IMP)testFunc, "i@:@");
* `i`表示方法的返回类型,下面是`dash`中的截图


* 第一个@是必须有的,表示self. 也就是说,上面说的返回类型+@是必备的。
:@ 表示传入的参数。如果有两个参数,就是:@@.可参见下面的代码

BOOL class_addMethod(Class cls,SEL name,IMP imp,    const char *types)   

/**
  * 一个参数
  *
  */

int cfunction(id self, SEL _cmd, NSString *str) {
    NSLog(@"%@", str);
    return10;//随便返回个值
}

-(void) oneParam {  
    TestClass *instance = [[TestClassalloc]init];
    //    方法添加
    class_addMethod([TestClassclass],@selector(ocMethod:), (IMP)cfunction,"i@:@");
    
    if ([instance respondsToSelector:@selector(ocMethod:)]) {
        NSLog(@"Yes, instance respondsToSelector:@selector(ocMethod:)");
    } else
        NSLog(@"Sorry");
    
    int a = (int)[instance ocMethod:@"我是一个OC的method,C函数实现"];
    NSLog(@"a:%d", a);
}

 /**
  * 两个参数
  *
  */

int cfunctionA(id self, SEL _cmd, NSString *str, NSString *str1) {
    NSLog(@"%@-%@", str, str1);
    return20;//随便返回个值
}
-(void) twoParam {  
    TestClass *instance = [[TestClass alloc]init];   
    class_addMethod([TestClass class],@selector(ocMethodA::), (IMP)cfunctionA,"i@:@@");
    
    if ([instance respondsToSelector:@selector(ocMethodA::)]) {
        NSLog(@"Yes, instance respondsToSelector:@selector(ocMethodA::)");
    } else
        NSLog(@"Sorry");
    int a = (int)[instance ocMethodA:@"我是一个OC的method,C函数实现" :@"-----我是第二个参数"];
    NSLog(@"a:%d", a);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值