本系列主要参考资料:
Objective-C Runtime Reference
Objective-C Runtime Programming Guide
涉及主要文件:objc/message.h,objc/objc-api.h,objc/objc.h,objc/runtime.h
特酷吧[tekuba.net]采用"署名-非商业用途-保持一致"的创作共用协议,使用本文内容请遵循该协议
Objective-C Runtime是Objective-C的基础内容,理解了Objective-C Runtime对于掌握Objective-C的很多技术原理非常有用。特酷吧特别整理了Objective-C Runtime的内容,共六篇,本文是第二篇:
《Objective-C Runtime分析(一)-Runtime初步》
《Objective-C Runtime分析(二)-Class,Method,SEL,IMP》
《Objective-C Runtime分析(三)-objc_msgSend》
《Objective-C Runtime分析(四)--Dynamic Method Resolution》
《Objective-C Runtime分析(五)-Message Forwarding》
《Objective-C Runtime分析(六)-Type Encodings & Declared Properties》
本文参考地址: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/
转载请注明来自特酷吧,本文地址:www.tekuba.net/program/336/
推荐阅读:
XCode lipo命令使用的一点知识
UINavigationController弹出(pop)和压入(push)操作的一个问题
IOS后台运行浅析
IOS7 Background Fetch后台应用程序刷新
IOS NSProcessInfo获取系统开机累计时间
想及时获取特酷吧的更新?想了解iOS,android开发最新技术动态,点击或扫描下方二维码下载“多识阅读”App,丰富的iOS,Android,Web等领域开发者博客随你订阅。