iOS笔记系列目录
概念
SEL
方法名(编号)
IMP
一个函数指针,保存了方法的地址
@selector(方法名)
获取方法的编号,结果是SEL类型。他的行为基本可以等同于C语言中的函数指针
区别
C语言中,可以直接把函数名赋值给一个函数指针,而且函数指针直接保存了函数地址
Objc中的类不能直接应用函数指针,只能使用@selector来获取,获取的是方法的编号
方法以@selector作为索引,@selector的数据类型是SEL,对应每个方法的编号,当我们寻找方法的时候使用的是这个方法编号。类中存在一个methodLists专门用来存放方法实现IMP和SEL的映射。方法编号SEL通过Dispatch table表寻找到对应的IMP,IMP就是一个函数指针,然后执行这个方法。
struct objc_class {
struct objc_class super_class; /*父类*/
const char *name; /*类名字*/
long version; /*版本信息*/
long info; /*类信息*/
long instance_size; /*实例大小*/
struct objc_ivar_list *ivars; /*实例参数链表*/
struct objc_method_list **methodLists; /*方法链表*/
struct objc_cache *cache; /*方法缓存*/
struct objc_protocol_list *protocols; /*协议链表*/
};
typedef struct objc_method *Method;
typedef struct objc_ method {
SEL method_name; //方法名
char *method_types; //参数类型
IMP method_imp; //方法实现
};
相关
class 返回对象的类;
isKindOfClass 和 isMemberOfClass检查对象是否在指定的类继承体系中;
respondsToSelector 检查对象能否相应指定的消息;
conformsToProtocol 检查对象是否实现了指定协议类的方法;
methodForSelector 返回指定方法实现的地址。
performSelector:withObject 执行SEL 所指代的方法。
具体实现
在寻找IMP的地址时,runtime提供了两种方法:
IMP class_getMethodImplementation(Class cls, SEL name);
IMP method_getImplementation(Method m)
而根据官方描述,第一种方法可能会更快一些
class_getMethodImplementation may be faster than method_getImplementation(class_getInstanceMethod(cls, name)).
(一)、对于第一种方法而言,类方法和实例方法实际上都是通过调用class_getMethodImplementation()
来寻找IMP地址的,不同之处在于传入的第一个参数不同
/// 类方法(假设有一个类ClassA)
class_getMethodImplementation(objc_getMetaClass("ClassA"),@selector(methodName));
/// 实例方法
class_getMethodImplementation([ClassA class],@selector(methodName));
通过该传入的参数不同,找到不同的方法列表,方法列表中保存着下面方法的结构体,结构体中包含这方法的实现,selector本质就是方法的名称,通过该方法名称,即可在结构体中找到相应的实现。
(二)、对于第二种方法而言,传入的参数只有method,区分类方法和实例方法在于封装method的函数
/// 类方法
Method class_getClassMethod(Class cls, SEL name)
/// 实例方法
Method class_getInstanceMethod(Class cls, SEL name)
/// 获取IMP地址
IMP method_getImplementation(Method m)
(三)、测试代码
@implementation TestSelAndImp
- (instancetype)init {
self = [super init];
if (self) {
[self getIMPFromSelector:@selector(aaa)];
[self getIMPFromSelector:@selector(test1)];
[self getIMPFromSelector:@selector(test2)];
}
return self;
}
- (void)test1 {
NSLog(@"test1");
}
- (void)test2 {
NSLog(@"test2");
}
- (void)getIMPFromSelector:(SEL)aSelector {
// First method, use 'class_getMethodImplementation'
IMP classInstanceIMP_1 = class_getMethodImplementation(objc_getClass("TestSelAndImp"), aSelector);
IMP metaClaseIMP_1 = class_getMethodImplementation(objc_getMetaClass("TestSelAndImp"), aSelector);
// Second method, use 'method_getImplementation'
Method classInstanceMethod_2 = class_getInstanceMethod(objc_getClass("TestSelAndImp"), aSelector);
IMP classInstnceIMP_2 = method_getImplementation(classInstanceMethod_2);
Method classMethod_2 = class_getClassMethod(objc_getClass("TestSelAndImp"), aSelector);
IMP classIMP_2 = method_getImplementation(classMethod_2);
Method metaClassMethod_2 = class_getClassMethod(objc_getMetaClass("TestSelAndImp"), aSelector);
IMP metaClassIMP_2 = method_getImplementation(metaClassMethod_2);
NSLog(@"selectorName: %@, classInstanceIMP_1: %p",NSStringFromSelector(aSelector), classInstanceIMP_1);
NSLog(@"selectorName: %@, metaClaseIMP_1: %p",NSStringFromSelector(aSelector),metaClaseIMP_1);
NSLog(@"selectorName: %@, classInstnceIMP_2: %p",NSStringFromSelector(aSelector),classInstnceIMP_2);
NSLog(@"selectorName: %@, classIMP_2: %p,",NSStringFromSelector(aSelector), classIMP_2);
NSLog(@"selectorName: %@, metaClassIMP_2: %p",NSStringFromSelector(aSelector),metaClassIMP_2);
NSLog(@"-------");
}
@end
打印结果:
调用class_getMethodImplementation()
方法时,无法找到对应实现时返回的相同的一个地址,无论该方法是在实例方法或类方法,无论是否对一个实例调用该方法,返回的地址都是相同的,但是每次运行该程序时返回的地址并不相同,而对于另一种方法method_getImplementation()
,如果找不到对应的实现,则返回0。
还有class_getClassMethod()
的第一个参数无论传入objc_getClass()
还是objc_getMetaClass()
,最终调用method_getImplementation()
都可以成功的找到类方法的实现。
而class_getInstanceMethod()
的第一个参数如果传入objc_getMetaClass()
,再调用method_getImplementation()
时无法找到实例方法的实现却可以找到类方法的实现。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。