Objective-C Runtime (一):类与对象
Runtime介绍
Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。这意味着它不仅需要一个编译器,也需要一个运行时系统来动态得创建类和对象、进行消息传递和转发。这个运行时系统即Objc Runtime。Objc Runtime其实是一个Runtime库,它基本上是用C和汇编写的,这个库使得C语言有了面向对象的能力。
Runtime库主要做下面几件事:
封装
:在这个库中,对象可以用C语言中的结构体表示,而方法可以用C函数 来实现,另外再加上了一些额外的特性。这些结构体和函数被runtime函数封装后,我们就可以在程序运行时创建,检查,修改类、对象和它们的方法了。消息传递
:当程序执行[object doSomething]时,会向消息接收者(object)发送一条消息(doSomething),runtime会根据消息接收者是否能响应该消息而做出不同的反应。这将在后面详细介绍。
在接下来的一系列文章中,我将介绍一下runtime
的基本工作原理以及在我们的代码里可以用它来做什么。在本文中,我们先来了解一下类与对象。
类与对象基础数据结构
###OC对象 OC的对象主要可以分为三种:
- instance对象(实例对象)
- class对象(类对象)
- meta-class对象(元类对象):主要存储着isa指针,superclass指针,类的类方法的信息(class method)。
instance对象 它主要是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象。
NSObjcet *object1 = [[NSObjcet alloc] init];
instance对象
在内存中存储的信息包括:
- isa指针
- 成员变量
class对象 我们通过class方法或runtime方法得到一个class对象。class对象也就是类对象。==每一个类在内存中有且只有一个class对象==。
Class objectClass1 = [NSObject class];
// runtime
Class objectClass2 = object_getClass(object1);
class对象
在内存中存储的信息主要包括:
- isa指针
- superclass指针
- 类的属性信息(@property)
- 类的成员变量信息(ivar)
- 类的对象方法信息(instance method)
- 类的协议信息(protocol)
meta-class对象 我们通过runtime方法得到一个class对象。==每个类在内存中有且只有一个meta-class对象。==
//runtime中传入类对象此时得到的就是元类对象
Class objectMetaClass = object_getClass([NSObject class]);
// 判断该对象是否为元类对象
class_isMetaClass(objectMetaClass)
meta-class对象
在内存中存储的信息主要包括:
- isa指针
- superclass指针
- 类的类方法的信息(class method)
我们来看看下面的图,来分析这三者的关系
从上图中,我们可以分析出:
- instance对象的isa指向class
- class的isa指向meta-class
- meta-class的isa指向基类的meta-class,基类的isa指向自己
- class的superclass指向父类的class,如果没有父类,superclass指针为nil
- meta-class的superclass指向父类的meta-class,基类的meta-class的superclass指向基类的class
- instance对象调用对象方法的轨迹,isa找到class,方法不存在,就通过superclass找父类
- 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;// 类的版本信息,默认为0long 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 Encodingsclass_copyIvarLis
t函数,它返回一个指向成员变量信息的数组。这个数组不包含在父类中声明的变量。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; /**
objc_property_attribute_t
其实是对属性的详细描述,包括属性名称、属性编码类型、原子类型/非原子类型等。如T@"NSString",C,N,V_title
,TQ,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。
这些字符串是什么意思,通过苹果官方文档我们可以分析出:
- 第一个位置是返回值类型,比如第一个方法返回值是V,第二个的是@,第三个的是v
- 第二/三个位置是第一/二个参数,参数列表从左到右算。分别是@ :,@ :,@ :,都是对象,其实第一个和第二个参数是固定的,第一个是接收消息的对象,而第二个是方法选择器SEL。
- 如果还有其它参数,依次…
- Type Encodings
###7. cache 用于缓存最近使用的方法。一个接收者对象接收到一个消息时,它会根据isa指针
去查找能够响应这个消息的对象。在实际使用中,这个对象只有一部分方法是常用的,很多方法其实很少用或者根本用不上。这种情况下,如果每次消息来时,我们都是methodLists
中遍历一遍,性能势必很差。这时,cache
就派上用场了。在我们每次调用过一个方法后,这个方法就会被缓存到cache
列表中,下次调用的时候runtime就会优先去cache
中查找,如果cache
没有,才去methodLists
中查找方法。这样,对于那些经常用到的方法的调用,但提高了调用的效率。
参考:
- tech.yunyingxbs.com/article/det…
- developer.apple.com/library/con…