博客地址:张飞的技术博客
我们都知道OC是在C语言的基础上增加了面向对象的编程特性。我们知道C语言并没有面向对象中的类的概念。细心的小伙伴会发现,C语言的结构体和类长得有点像。难道OC中的类和C语言的结构体有渊源?是的,你没有猜错,OC是一门动态语言,有一套运行时机制,会把OC中的类动态构造为C语言的结构体,不信?且往下看。Objective-C类是由Class类型来表示的。在objc.h
中可以找到OC中类的定义,如下:
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
从这里可以看出来OC中的类其实是C语言中的一个结构体指针,它指向了objc_class
这个结构体。进一步跟踪,在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; //类的版本
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
} OBJC2_UNAVAILABLE;
详细看一下结构体的成员变量是干啥的吧。
-
isa
:每一个对象都有一个叫isa的指针类型的成员变量,它指向该对象的实际类型(比如有一个Person类,他有一个p1的实例对象,那么这个p1的成员变量isa指向的就是Person类),需要注意的是在Objective-C中,所有的类自身也是一个对象,这个对象的Class里面也有一个isa指针,它指向metaClass(元类)。 -
super_class
:和isa一样,也是一个指针类型的,它指向了自己的父类。那NSObject类的super_class指向什么呢?答案是指向NULL。 -
name
:这个是记录类的名字用的。 -
version
:记录类的版本用的。这对于对象的序列化非常有用,它可是让我们识别出不同类定义版本中实例变量布局的改变。 -
info
:类的信息,主要作用是在运行期间做一些标识。 -
instance_size
:记录类实例对象的占用空间的大小。 -
ivars
:类的成员变量不可能总是一个,所以类的成员变量就用了一个链表来记录。我们可以看到这个objc_ivar_list
是一个结构体,在runtime.h
的1655行的位置就能找到objc_ivar_list
这个结构体的声明:
struct objc_ivar_list {
int ivar_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
/* variable length structure */
struct objc_ivar ivar_list[1] OBJC2_UNAVAILABLE;
}
在objc_ivar_list
这个结构体里面还有一个objc_ivar
的结构体,它的声明就objc_ivar_list
这个结构体的上面,声明是这个样子的:
struct objc_ivar {
char *ivar_name OBJC2_UNAVAILABLE;
char *ivar_type OBJC2_UNAVAILABLE;
int ivar_offset OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
}
-
objc_method_list
:类的方法也和成员变量一个道理,因此也是用一个链表来记录的。也许你会疑惑为什么objc_method_list
是指针的指针,先看一看它的结构体实现吧,位置是runtime.h
的1671行:
struct objc_method_list {
struct objc_method_list *obsolete OBJC2_UNAVAILABLE;
int method_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
/* variable length structure */
struct objc_method method_list[1] OBJC2_UNAVAILABLE;
}
在这个结构体的声明里面可以看到里面有一个叫objc_method_list
的结构体指针,这样我们不就不难理解它是一个指针的指针了。这里面的结构体objc_method
的定义如下:
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
}
-
cache
:cache是用来记录方法调用的,每当调用方法的时候就先到这里查一下这个方法有没有被调用过。如果被调用过就直接想这个方法发送消息,如果没有才去methodLists
里面去查找。同样是在runtime.h
中我们找到结构体objc_cache
的定义:
struct objc_cache {
unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
unsigned int occupied OBJC2_UNAVAILABLE;
Method buckets[1] OBJC2_UNAVAILABLE;
};
也许你会发现一个Method
的类,其实他的本质就是上面的objc_method
这个结构体。
-
protocols
:和方法链表,成员变量一样。用来记录协议的链表。它的定义如下:
struct objc_protocol_list {
struct objc_protocol_list *next;
long count;
Protocol *list[1];
};
这下明白了吧,OC的类和C语言还有这么一层关系,当然这都归功于OC的Runtime机制。
在面向对象编程中不仅有类的概念,还有实例对象的概念,既然类与C语言的结构体有着千丝万缕的关系,那么,类对象想必也和C语言撇不开关系吧。是的,有什么关系呢?往下看!
上面说了类是怎么构造的,那你不禁会问类对象是怎么构造的呢?其实就在objc.h
文件里声明Class结构体下面就可以发现这样的代码:
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
从注释中不难看出,结构体objc_object
表示的是类对象,可以看到,这个结构体只有一个成员变量,即指向其类的isa指针。这样,当我们向一个Objective-C对象发送消息时,运行时库会根据实例对象的isa指针找到这个实例对象所属的类。Runtime库会在类的方法列表及父类的方法列表中去寻找与消息对应的selector指向的方法。找到后即运行这个方法。也就是是说当我们创建一个特定类的实例对象时,分配的内存包含一个objc_object数据结构,然后是类的实例变量的数据。
结尾
上面讲的都要归功于牛逼的Runtime,那么Runtime有什么🐂👃的呢?有兴趣就看我的下一片文章。最后大家不要忘了,如果我的文章对你有帮助,你愿意请我喝杯茶的话,请用支付宝扫一扫下面的二维码随意打赏。祝大家玩得愉快!
网友评论