Objective-C Runtime (一):类与对象

Objective-C Runtime (一):类与对象

Runtime介绍html

Objective-C语言是一门动态语言,它将不少静态语言在编译和连接时期作的事放到了运行时来处理。这意味着它不只须要一个编译器,也须要一个运行时系统来动态得建立类和对象、进行消息传递和转发。这个运行时系统即Objc Runtime。Objc Runtime实际上是一个Runtime库,它基本上是用C和汇编写的,这个库使得C语言有了面向对象的能力。数组

Runtime库主要作下面几件事:缓存

  1. 封装:在这个库中,对象能够用C语言中的结构体表示,而方法能够用C函数 来实现,另外再加上了一些额外的特性。这些结构体和函数被runtime函数封装后,咱们就能够在程序运行时建立,检查,修改类、对象和它们的方法了。
  2. 消息传递:当程序执行[object doSomething]时,会向消息接收者(object)发送一条消息(doSomething),runtime会根据消息接收者是否能响应该消息而作出不一样的反应。这将在后面详细介绍。

在接下来的一系列文章中,我将介绍一下runtime的基本工做原理以及在咱们的代码里能够用它来作什么。在本文中,咱们先来了解一下类与对象。bash

类与对象基础数据结构

###OC对象 OC的对象主要能够分为三种:数据结构

  1. instance对象(实例对象)
  2. class对象(类对象)
  3. meta-class对象(元类对象):主要存储着isa指针,superclass指针,类的类方法的信息(class method)。

instance对象 它主要是经过类alloc出来的对象,每次调用alloc都会产生新的instance对象。app

NSObjcet *object1 = [[NSObjcet alloc] init];
复制代码

instance对象在内存中存储的信息包括:ide

  1. isa指针
  2. 成员变量

class对象 咱们经过class方法或runtime方法获得一个class对象。class对象也就是类对象。==每个类在内存中有且只有一个class对象==。函数

Class objectClass1 = [NSObject class];
// runtime
Class objectClass2 = object_getClass(object1);
复制代码

class对象在内存中存储的信息主要包括:性能

  1. isa指针
  2. superclass指针
  3. 类的属性信息(@property)
  4. 类的成员变量信息(ivar)
  5. 类的对象方法信息(instance method)
  6. 类的协议信息(protocol)

meta-class对象 咱们经过runtime方法获得一个class对象。==每一个类在内存中有且只有一个meta-class对象。==ui

//runtime中传入类对象此时获得的就是元类对象
Class objectMetaClass = object_getClass([NSObject class]);
// 判断该对象是否为元类对象
class_isMetaClass(objectMetaClass) 
复制代码

meta-class对象在内存中存储的信息主要包括:

  1. isa指针
  2. superclass指针
  3. 类的类方法的信息(class method)

咱们来看看下面的图,来分析这三者的关系

从上图中,咱们能够分析出:

  1. instance对象的isa指向class
  2. class的isa指向meta-class
  3. meta-class的isa指向基类的meta-class,基类的isa指向本身
  4. class的superclass指向父类的class,若是没有父类,superclass指针为nil
  5. meta-class的superclass指向父类的meta-class,基类的meta-class的superclass指向基类的class
  6. instance对象调用对象方法的轨迹,isa找到class,方法不存在,就经过superclass找父类
  7. class调用类方法的轨迹,isa找meta-class,方法不存在,就经过superclass找父类

###Class

Objective-C类是由Class类型来表示的,它其实是一个指向objc_class结构体的指针。在objc/objc.h它的定义以下:

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
复制代码

objc/runtime.h中咱们能够看到结构体objc_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;// 类的版本信息,默认为0
    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;
复制代码

###1. name 类名

// 获取类的类名
const char * class_getName ( Class cls );
复制代码

###2. isa 在Objective-C中,类自身也是一个对象,咱们称之为类对象,在这个类对象中,也有一个isa指针,它指向了metaClass(元类)

###3. super_class 指向该类的父类,若是该类已是最顶层的根类(如NSObject或NSProxy),则super_class为nil。

###4. instance_size

// 获取实例大小
size_t class_getInstanceSize ( Class cls );
复制代码

###5. objc_ivar_list 成员变量和属性信息。ivars是一个数组,全部的成员变量、属性的信息是放在链表ivars中的。

######获取成员变量信息

// 获取整个成员变量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );
//获取一个成员变量的名字
const char * _Nullable ivar_getName(Ivar _Nonnull v) 
//获取一个成员变量的
const char * _Nullable ivar_getTypeEncoding(Ivar _Nonnull v) 
复制代码
  • ivar_getTypeEncoding函数获取的是变量的数据类型,详细的说明能够看苹果官方文档Type Encodings
  • class_copyIvarList函数,它返回一个指向成员变量信息的数组。这个数组不包含在父类中声明的变量。outCount指针返回数组的大小。须要注意的是,咱们必须使用free()来释放这个数组。

######获取成员变量属性信息

/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;
//获取整个成员变量属性列表
objc_property_t _Nonnull *_Nullable class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount)

/// Defines a property attribute
typedef struct {
    const char * _Nonnull name;           /**< The name of the attribute */
    const char * _Nonnull value;          /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;

objc_property_attribute_t * _Nullable property_copyAttributeList(objc_property_t _Nonnull property,unsigned int * _Nullable outCount)
复制代码
  • objc_property_attribute_t实际上是对属性的详细描述,包括属性名称、属性编码类型、原子类型/非原子类型等。如T@"NSString",C,N,V_titleTQ,N,V_age

下面详细地说明property_getAttributes获取到的属性的语义:

  • 第一:T老是第一个,它表明类型。 对于类类型,它都是T@类型,其中@表示对象类型;对于id类型,它就是@;而对于基本数据类型,它都是T加上编码类型(@encode(type)),好比int类型就是Ti。
  • 第二:表示属性编码类型,好比是C表示copy,&表示strong,W表示weak等。如果基本类型,它默认是assign,所以此时它是空的。
  • 第三:指定是nonatomic仍是atomic,如果nonatomic,则用N表示,如果atomic,则值为空。好比的count属性的详细描述为:Ti,N,V_count,它表示int类型、nonatomic、成员变量名为_count;tomicProperty属性描述为:T@NSNumber,&,V_atomicProperty,它表示类型为NSNumber且为对象类型、strong、atomic、成员变量名为_atomicProperty。
  • 第四:在详细描述中,属性名称是V开头,后面跟着成员变量名称。

属性的详细说明Property Type

###6. methodLists 对象方法列表。

/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;

struct objc_method {
    SEL method_name;        // 方法名称
    char *method_typesE;    // 参数和返回类型的描述字串
    IMP method_imp;         // 方法的具体的实现的指针
}

复制代码

获取method信息:

//方法列表
Method _Nonnull * _Nullable class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount) 
//方法名
SEL _Nonnull method_getName(Method _Nonnull m) 
//方法实现
IMP _Nonnull method_getImplementation(Method _Nonnull m) 
//方法参数和返回类型的信息
const char * _Nullable method_getTypeEncoding(Method _Nonnull m) 
//方法参数数量
unsigned int method_getNumberOfArguments(Method _Nonnull m)
//方法参数类型
char * _Nullable method_copyArgumentType(Method _Nonnull m, unsigned int index) 
复制代码
  • method_getTypeEncoding获取函数的编码,其结果是一串值,如:v16@0:8;@16@0:8;v24@0:8@16。

这些字符串是什么意思,经过苹果官方文档咱们能够分析出:

  1. 第一个位置是返回值类型,好比第一个方法返回值是V,第二个的是@,第三个的是v
  2. 第二/三个位置是第一/二个参数,参数列表从左到右算。分别是@ :,@ :,@ :,都是对象,其实第一个和第二个参数是固定的,第一个是接收消息的对象,而第二个是方法选择器SEL。
  3. 若是还有其它参数,依次…
  4. Type Encodings

###7. cache 用于缓存最近使用的方法。一个接收者对象接收到一个消息时,它会根据isa指针去查找可以响应这个消息的对象。在实际使用中,这个对象只有一部分方法是经常使用的,不少方法其实不多用或者根本用不上。这种状况下,若是每次消息来时,咱们都是methodLists中遍历一遍,性能势必不好。这时,cache就派上用场了。在咱们每次调用过一个方法后,这个方法就会被缓存到cache列表中,下次调用的时候runtime就会优先去cache中查找,若是cache没有,才去methodLists中查找方法。这样,对于那些常常用到的方法的调用,但提升了调用的效率。

参考:

  1. tech.yunyingxbs.com/article/det…
  2. developer.apple.com/library/con…
相关文章
相关标签/搜索