本文参考地址:https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtHowMessagingWorks.html
本文主要分析了与Objective-C Runtime密切相关的几个数据类型/概念:Class , Method,,SEL , IMP ,他们都在objc/objc.h中定义。先来看看他们的定义。
typedef struct objc_class *Class;
typedef struct objc_object {
Class isa;
} *id;//可以看到,iOS中很重要的id实际上就是objc_object的指针.而NSObject的第一个对象就是Class类型的isa。因此id可以标示所有基于NSObject的对象。
typedef struct objc_selector *SEL;
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id (*IMP)(id, SEL, ...);
#endif
一,Class
Class 被定义为一个指向objc_class的结构体指针,表示一个类的类结构。objc_class在objc/objc_class.h中定义如下:
struct objc_class {
Class isa;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;/*父类*/
const char *name OBJC2_UNAVAILABLE;/*类名称*/
long version OBJC2_UNAVAILABLE;/*版本信息*/
long info OBJC2_UNAVAILABLE;/*类信息*/
long instance_size OBJC2_UNAVAILABLE;/*实例大小*/
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;/*实例参数链表*/
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;/*类方法链表*/
struct objc_cache *cache OBJC2_UNAVAILABLE;/*类方法缓存*/
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;/*协议链表*/
#endif
} OBJC2_UNAVAILABLE;
可见,Class是指向类结构体的指针,该类结构体含有一个指向其父类类结构的指针,该类方法的链表,该类方法的缓存以及其他信息。
NSObject 的class方法就返回这样一个指向其类结构的指针。每一个基于NSObject的类实例对象都有一个指向该对象的类结构的指针,叫做isa。通过该指针,对象可以访问它对应的类以及相应的父类。
二,Method
Method是Runtime内部定义的方法,Class中定义有一个objc_method_list,链表都是objc_method类型的,定义如下:
typedef struct objc_method *Method;
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;/*标示方法名称*/
char *method_types OBJC2_UNAVAILABLE;/*方法的参数类型*/
IMP method_imp OBJC2_UNAVAILABLE;/*指向该方法的具体实现的函数指针*/
}
struct objc_method_list {
struct objc_method_list *obsolete OBJC2_UNAVAILABLE;
int method_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
/* variable length structure */
struct objc_method method_list[1] OBJC2_UNAVAILABLE;
}
三,SEL
定义如下:
typedef struct objc_selector *SEL;
标示该方法的名字/签名
示例:
-(void)helloTekuba:(NSString *)url port:(int)port
{
NSLog(@"%@,%d",url,port);
}
NSLog(@"SEL = %s",@selector(helloTekuba:port:));
打印结果:
SEL = helloTekuba:port:
不同的类可以拥有相同的selector,不同类的实例对象performSelector相同的selector时,会在各自的方法链表中根据 selector 去查找具体的方法实现IMP, 然后用这个方法实现去执行具体的实现代码。这是一个动态绑定的过程,在编译的时候,我们不知道最终会执行哪一些代码,只有在执行的时候,通过selector去查询,我们才能确定具体的执行代码。
四,IMP
typedef id (*IMP)(id, SEL, ...);
我们知道 id是一个指向 objc_object 结构体的指针(请看本文前面objc_object的定义),该结构体只有一个成员isa,所以任何继承自 NSObject 的类对象都可以用id 来指代,因为 NSObject 的第一个成员实例就是isa。
IMP 是一个函数指针,这个被指向的函数包含一个接收消息的对象id, 调用方法的选标 SEL,以及不定个数的方法参数,并返回一个id。也就是说IMP是消息最终调用的执行代码,是方法真正的实现代码 。我们可以像在C语言里面一样使用这个函数指针。
NSObject 类中的methodForSelector:方法就是这样一个获取指向方法实现IMP 的指针,methodForSelector:返回的指针和赋值的变量类型必须完全一致,包括方法参数类型和返回值类型。
五,其他
Ivar
Runtime中用来表示instance variable,实例变量,跟某个对象关联,不能被静态方法使用,与之想对应的是class variable,其声明如下:
typedef struct objc_ivar *Ivar;
Category
Runtime中用来表示Category,其声明为:
typedef struct objc_category *Category;
Catagory可以动态地为已经存在的类添加新的行为。这样可以保证类的原始设计规模较小,功能增加时再逐步扩展。使用Category对类进行扩展时,不需要访问其源代码,也不需要创建子类。关于更多Catagory的知识可以参考:http://www.tekuba.net/program/312/