OC学习之Runtime之一类与对象

坚持 成长 每日一篇

Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。这种动态语言的优势在于:我们写代码时能够更具灵活性,如我们可以把消息转发给我们想要的对象,或者随意交换一个方法的实现等。(个人认为这样有优点也有缺点,就是在很多错误会在运行时候才被发现,与C#等可以在编译时候就发现程序的错误,OC这个也算是缺点吧)

OC通过一个运行时系统即runtime来实时执行编译的动作,runtime系统就像一个守护进程一样保证程序运行。runtime是一个C和汇编写成的库,使C语言具有面向对象的能力。

Runtime使用C语言结构体表示对象,用C语言函数表示方法,这些C语言函数和结构体被Runtime封装后,我们就可以在程序中执行创建,检查,修改类和对象和他们的方法。

当程序执行时,会向程序接收者发送(object)发送一条消息,runtime根据接收者是否响应该消息做出不同的响应。

Objective-C runtime目前有两个版本:Modern runtime(64位版本)和Legacy runtime(32位版本,可以忽略了)。

OC的类

OC的Class其实是一个objc_class结构体的指针,下面是Class类的定义

typedef struct objc_class *Class;

查看objc/runtime.h中objc_class结构体的定义如下:

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY; //元类指针

#if !__OBJC2__
    Class super_class                       OBJC2_UNAVAILABLE;  // 父类
    const char *name                        OBJC2_UNAVAILABLE;  // 类名
    long version                            OBJC2_UNAVAILABLE;  // 类的版本信息,默认为0
    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

}

注意:由此可见在OC中类其实就是一个C结构体指针,又因为Runtime用C结构体表示对象,我们可以认为类本身就是一个对象。

下面介绍该结构体的一些属性:
isa:需要注意的是在Objective-C中,所有的类自身也是一个对象,这个对象的Class里面也有一个isa指针,它指向metaClass(元类)。
注意:我们可以认为所有类都有元类,所有元类的元类指向根类的元类(即根元类)。根元类的元类指向自身。

super_class:指向该类的父类,如果该类已经是最顶层的根类(如NSObject或NSProxy),则super_class为NULL。根元类的super_class指向根类,类的元类的super_class指向父类的元类。

version:我们可以使用这个字段来提供类的版本信息。这对于对象的序列化非常有用,它可是让我们识别出不同类定义版本中实例变量布局的改变。

cache:用于缓存最近使用的方法。用户调用方法时候会先遍历cache里面的方法再遍历methodLists里的方法。一个方法调用时候,如果不存在cache里,而是在methdLists里被找到,在这个方法调用结束时候会被保存到cache里。这样子就实现了常用方法不需要去遍历list,以便提高效率。

对象和id类

OC里的每一个对象都是用objc_object结构体来表示,定义如下

struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

可以看到,这个结构体只有一个字体,即指向其类的isa指针。这样,当我们向一个Objective-C对象发送消息时,运行时库会根据实例对象的isa指针找到这个实例对象所属的类。Runtime库会在类的方法列表及父类的方法列表中去寻找与消息对应的selector指向的方法。找到后即运行这个方法。

id类型的定义

typedef struct objc_object *id;

这就是为什么id类型能指向任意对象的原因。

结构体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;
};

mask:一个整数,指定分配的缓存bucket的总数。在方法查找过程中,Objective-C runtime使用这个字段来确定开始线性查找数组的索引位置。指向方法selector的指针与该字段做一个AND位操作(index = (mask & selector))。这可以作为一个简单的hash散列算法。
occupied:一个整数,指定实际占用的缓存bucket的总数。
buckets:指向Method数据结构指针的数组。这个数组可能包含不超过mask+1个元素。需要注意的是,指针可能是NULL,表示这个缓存bucket没有被占用,另外被占用的bucket可能是不连续的。这个数组可能会随着时间而增长。

元类(MetaClass)

在上面我们提到,所有的类自身也是一个对象,我们可以向这个对象发送消息(即调用类方法)。如:
NSArray *array = [NSArray array];
类既然是一个对象,那么他也就是一个objc_object指针,那么他的isa指针指向的是该类的元类。元类的存在就是保存该类的类方法。让类也能像对象一样被反问。
下面是元类和常规类关系图
这里写图片描述

类与对象操作函数

runtime提供了大量的函数来操作类与对象。类的操作方法大部分是以class为前缀的,而对象的操作方法大部分是以objc或object_为前缀。下面我们将根据这些方法的用途来分类讨论这些方法的使用。

获取该类元类方法

OBJC_EXPORT Class objc_getClass(const char *name)//因为者里把类当对象所以是以objc开头

例子:

Class currentClass = objc_getClass((__bridge void*)[NSObject class])

获取类的名字

OBJC_EXPORT const char *class_getName(Class cls) 

例子:

 NSString *classString =  [NSString stringWithUTF8String:class_getName(objc_getClass((__bridge void*)[NSArray class]) )] ;

获取类的父类

class_getSuperclass函数,当cls为Nil或者cls为根类时,返回Nil。

Class class_getSuperclass ( Class cls );

判断给定的Class是否是一个元类

class_isMetaClass函数,如果是cls是元类,则返回YES;如果否或者传入的cls为Nil,则返回NO。

BOOL class_isMetaClass ( Class cls );

获取实例大小

size_t class_getInstanceSize ( Class cls );
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值