OC 的Runtime 机制之类和对象

RunTime简称运行时。就是系统在运行的时候的一些机制,其中最主要的是消息机制。OC的函数调用成为消息发送。属于动态调用过程。在编译的时候并不能决定真正调用哪个函数(事实证明,在编 译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错。)只有在真正运行的时候才会根据函数的名称找 到对应的函数来调用。

1.下面我们来看最基本的类的定义
Class实际上是一个指向objc_class结构体的指针
class的定义:

typedef struct objc_class *Class;

class 是一个objc_class 结构类型的指针,id是一个objc_class类型的指针

objc_class

struct objc_class { 
struct objc_class* isa;
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; 
}; 

也就是说class的结构是上面那样的,objc_class的成员介绍如下:
isa:是一个objc_class类型的指针,其实是指向元类metaclass。
super_class : 一个指向父类的objc_class类型的指针,如果是最顶层,那么super_class 为 null
在继承关系中,通常会有,子类,父类,根类,他们的对应关系通常是这样的
类的实例对象的isa指向该类,该类的isa指向该类的父类,类的super_class指向其父类,如此一层一层指向

OC 的Runtime 机制之类和对象_第1张图片
1.jpg

class和metaclass
class是instance object 的类类型 。当我们向实例对象发送消息(实例方法 )的时候,我们在该实例对象的class的方法列表(methodlists )中查找相应函数,如果没有找到就去改类的父类的方法列表中查找,然后一层一层去查找,直到找到为止。

NSString * str;[str lowercaseString]; 
向 str 实例对象发送 lowercaseString 消息,会在 NSString 类结构的 methodlists 中去查找 lowercaseString 的响应 函数。 

metaclass是class object 类类型。当我们向类对象发送消息(类方法) 的时候,我们在该类对象的 metaclass 结构的 methodlists 中去查找响应的函数,如果没有找到匹配的响应函数则在该 metaclass 的父类中的 methodlists 去查找

[NSString stringWithString:@"str"]; 
向 NSString 类对象发送 stringWithString 消息,会在 NSString 的 metaclass 类结构的 methodlists 中去查找 stringWithString 的响应函数 

name:表示类的名称,通过这个名称查找到该类(通过:id objc_getClass(const char *aClassName))或该类的 metaclass(id objc_getMetaClass(const char *aClassName));
version:类的版本信息,可以在运行期对其进行修改(class_setVersion)或获 取(class_getVersion)。
info:供运行期使用的一些位标识。
有如下一些位掩码:CLS_CLASS (0x1L) 表示该类为普通 class ,其中包含实例方法和变量;
CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法;
CLS_INITIALIZED(0x4L) 表示该类已经被运行期初始化了,这个标识位只被 objc_addClass 所设置;
CLS_POSING (0x8L) 表示该类被 pose 成其他的类;(poseclass 在 ObjC 2.0 中被废弃了);
CLS_MAPPED (0x10L) 为 ObjC 运行期所使用CLS_FLUSH_CACHE (0x20L) 为 ObjC 运行期所使用
CLS_GROW_CACHE (0x40L) 为 ObjC 运行期所使用
CLS_NEED_BIND (0x80L) 为 ObjC 运行期所使用
CLS_METHOD_ARRAY (0x100L) 该标志位指示 methodlists 是指向一个objc_method_list 还是 一个包含 objc_method_list 指针的数组;

instance_size:该类的实例变量大小(包括从父类继承下来的实例变量);
ivars: 指向objc_ivar_list 的指针 ,储存每个实例变量的地址,如果没有的话是null
objc_method_list:方法列表
cache:方法的缓存
protocols:协议列表

2.对象
objc_object 与id

struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

typedef struct objc_object *id;

可以看出,objc_object结构体里只有一个字段,isa指针,当我们向一个obj对象发送消息的是时候,runtime会根据实例对象的isa指针找到这个实例对象所对应的类,然后去类的方法列表里找到对应的方法。
Id类型也是一个objc_object的结构类型的指针。

  1. objc_cache
    objc_cache结构体定义如下:
    struct objc_cache {
    unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
    unsigned int occupied OBJC2_UNAVAILABLE;
    Method buckets[1] OBJC2_UNAVAILABLE;
    };

1.mask:是一个int类型整数,指定分配缓存的bucket的总数,在方法查找的过程中,runtime使用这个字段来确定开始线性查找数组的索引位置。可以作为一个简单的hash算法
2.occupied:一个int类型整数,指定实际占用缓存的bucket的总数
3.buckets:指向method数据结构指针的数组的指针,这个数组可能为null,表示这个缓存bucket没有占用。

4.关于metaclass(元类):
meta-class是一个类对象的类。
当我们向一个对象发送消息的时候,runtime会在这个对象所属的这个类的方法列表中查找方法,如果我们向一个类发送消息的时候,会在这个类的meta-class的方法列表中查找。
Meta-class储存着一个类的所有的类方法。
再深入一下,meta-class也是一个类,也可以向它发送一个消息,那么它的isa又是指向什么呢?为了不让这种结构无限延伸下去,Objective-C的设计者让所有的meta-class的isa指向基类的meta-class,以此作为它们的所属类。即,任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而基类的meta-class的isa指针是指向它自己。这样就形成了一个完美的闭环。

5.类和对象的runtime函数总结
(1)类

类名:

// 获取类的类名
const char * class_getName ( Class cls );

对于class_getName函数,如果传入的cls为Nil,则返回一个字字符串。

父类和元类

// 获取类的父类
Class class_getSuperclass ( Class cls );
 
// 判断给定的Class是否是一个元类
BOOL class_isMetaClass ( Class cls );

class_getSuperclass函数,当cls为Nil或者cls为根类时,返回Nil。不过通常我们可以使用NSObject类的superclass方法来达到同样的目的。
class_isMetaClass函数,如果是cls是元类,则返回YES;如果否或者传入的cls为Nil,则返回NO。

实例变量大小(instance_size)

// 获取实例大小
size_t class_getInstanceSize ( Class cls );

成员变量(ivars)和属性

在obj_class中,所有的成员变量和属性的信息都是放在 struct objc_ivar_list* ivars中的,ivars是一个数组,数组中每个元素都是指向ivar的指针。

1.成员变量操作函数

// 获取类中指定名称实例成员变量的信息
Ivar class_getInstanceVariable ( Class cls, const char *name );
 
// 获取类成员变量的信息
Ivar class_getClassVariable ( Class cls, const char *name );
 
// 添加成员变量
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types );
 
// 获取整个成员变量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );

class_getInstanceVariable函数,它返回一个指向包含name指定的成员变量信息的objc_ivar结构体的指针(Ivar)。

class_getClassVariable函数,目前没有找到关于Objective-C中类变量的信息,一般认为Objective-C不支持类变量。注意,返回的列表不包含父类的成员变量和属性。

Objective-C不支持往已存在的类中添加实例变量,因此不管是系统库提供的提供的类,还是我们自定义的类,都无法动态添加成员变量。但如果我们通过运行时来创建一个类的话,又应该如何给它添加成员变量呢?这时我们就可以使用class_addIvar函数了。不过需要注意的是,这个方法只能在objc_allocateClassPair函数与objc_registerClassPair之间调用。另外,这个类也不能是元类。成员变量的按字节最小对齐量是1<

class_copyIvarList函数,它返回一个指向成员变量信息的数组,数组中每个元素是指向该成员变量信息的objc_ivar结构体的指针。这个数组不包含在父类中声明的变量。outCount指针返回数组的大小。需要注意的是,我们必须使用free()来释放这个数组。

2.属性操作

// 获取指定的属性
objc_property_t class_getProperty ( Class cls, const char *name );
 
// 获取属性列表
objc_property_t * class_copyPropertyList ( Class cls, unsigned int *outCount );
 
// 为类添加属性
BOOL class_addProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );
 
// 替换类的属性
void class_replaceProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );

方法methodLists

// 添加方法
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参数指向的函数)至少需要两个参数

void myMethodIMP(id self, SEL _cmd)
{
    // implementation ....
}

与成员变量不同的是,我们可以为类动态添加方法,不管这个类是否已存在
lass_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:方法来达到相同目的。

协议(objc_protocol_list)

// 添加协议
BOOL class_addProtocol ( Class cls, Protocol *protocol );
 
// 返回类是否实现指定的协议
BOOL class_conformsToProtocol ( Class cls, Protocol *protocol );
 
// 返回类实现的协议列表
Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount );

class_conformsToProtocol函数可以使用NSObject类的conformsToProtocol:方法来替代。
class_copyProtocolList函数返回的是一个数组,在使用后我们需要使用free()手动释放。

版本(version)

// 获取版本号
int class_getVersion ( Class cls );
 
// 设置版本号
void class_setVersion ( Class cls, int version );

你可能感兴趣的:(OC 的Runtime 机制之类和对象)