参考内容
Objective-C Runtime 运行时之三:方法与消息
方法中 SEL, IMP, Method 的定义与关系

SEL
- 代表方法的名称。仅以名字来识别。
- 不论两个类是否存在依存关系,只要他们拥有相同的方法名,那么他们的
SEL都是相同的。比如,有n个viewcontroller页面,每个页面都有一个viewdidload,每个页面的载入,肯定都是不尽相同的。但是我们可以通过打印,观察发现,这些viewdidload的SEL都是同一个:
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_getInstanceMethod、class_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:方法来达到相同目的。
示例,实现 method swizzle
用runtime + category 实现

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);
}