objcRuntime小记

2017-11-25

最近在学习objc的底层runtime知识,学到这块时就做个小笔记,做个总结。刚好最近也搭建了自己的博客,就以此作为个人博客的开篇吧

###什么是runtime

我们知道高级编程语言要想成为可执行文件的过程:高级语言 -> 汇编语言 -> 机器语言。机器语言是计算机唯一能识别的语言。所以从OC代码到最后的可执行文件的过程大概有:OC -> C - >编译(汇编) -> 可执行文件 。从OC到C的过程就是由runtime来实现。通俗的将runtime就是把面向对象的类转变为面向过程的结构体

我们导入import <objc/runtime.h>,查看runtime.h的头文件,会发现以下代码

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

通过这个代码发现,我们创建的对象其实就是一个objc_object的结构体,这个结构体只有一个成员变量(Class的isa指针),Classs是一个结构体指针,指向objc_class。而objc的类对象就是一个objc_class的结构体,这个结构体存放的数据被称为元数据(metadata)该结构体的第一个成员变量也是isa指针,这样就说明了类本身也是一个对象,我们趁为类对象。类对象的isa指针指向的对象是元类

objc函数调用总结

Objective-C是动态语言,编译时并不决定函数的调用,通俗的讲,所有的类型声明都是给编译器看的。OC的函数调用实际上就是向一个objc对象(或者Class)发消息,发消息实际上就是沿着它的isa指针寻找真正的函数地址

runtime中声明了id和Class的类型,简化如下

struct objc_class {
    struct objc_class *isa;
};
struct objc_object {
    struct objc_class *isa;
};
 
typedef struct objc_class *Class; //类  (class object)
typedef struct objc_object *id;   //对象 (instance of class)

在objc中,id代表了一个对象。凡是首地址是*isa的结构体指针,都可以被认为是objc的对象。运行时通过isa指针,可以查找到该对象是属于什么类 只要一个对象满足下面的结构,就可以对它发消息

struct objc_object {
    Class isa;
} *id
runtime的运行机制

我们可以通过clang来重写一个类查看cpp代码

clang -rewrite-objc ClassName.m
//class的实际结构
struct _class_t {
	struct _class_t *isa;        //isa指针
	struct _class_t *superclass; //父类
	void *cache;
	void *vtable;
	struct _class_ro_t *ro;     //Class包含的信息
};

//Class包含的信息
struct _class_ro_t {
	unsigned int flags;
	unsigned int instanceStart;
	unsigned int instanceSize;
	unsigned int reserved;
	const unsigned char *ivarLayout;
	const char *name; //类名
	const struct _method_list_t *baseMethods; //方法列表
	const struct _objc_protocol_list *baseProtocols;  //协议列表
	const struct _ivar_list_t *ivars; //ivar列表
	const unsigned char *weakIvarLayout;
	const struct _prop_list_t *properties; //属性列表
};

//Person(meta-class)
struct _class_t OBJC_METACLASS_$_Person  = {
	.isa        = &OBJC_METACLASS_$_NSObject,
	.superclass = &OBJC_METACLASS_$_NSObject,
	.cache      = (void *)&_objc_empty_cache,
	.vtable     = (void *)&_objc_empty_vtable,
	.ro         = &_OBJC_METACLASS_RO_$_Person, //包含了类方法等
};
 
//Person(Class)
struct _class_t OBJC_CLASS_$_Person = {
	.isa        = &OBJC_METACLASS_$_Person,   //此处isa指向meta-class
	.superclass = &OBJC_CLASS_$_NSObject,
	.superclass = (void *)&_objc_empty_cache,
	.vtable     = (void *)&_objc_empty_vtable,
	.ro         = &_OBJC_CLASS_RO_$_Person,   //包含了实例方法 ivar信息等
};
typedef struct objc_object Person;   //定义NyanCat类型

所有实例的isa都指向了Class,Class是一个全局变量,其中记录了类名,成员变量,property信息,实例方法列表,协议列表

Person *jack = [Person alloc] init];
[jack walk];

向jack(instance)发送walk消息时,运行时会通过isa指针查找到Person(class),Class中保存着本类中定义的实例方法的指针

[Person eat];

向Person发送eat消息时,runtime会通过isa查找到Person(meta-class),这里保存本类定义的类方法的指针

obj_msgSend()实际上是找到这个函数指针,然后调用它