美文网首页iOS RuntimeiOS
OC中的类是怎么来的?

OC中的类是怎么来的?

作者: FITZ9311 | 来源:发表于2015-11-14 18:50 被阅读300次

博客地址:张飞的技术博客
我们都知道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有什么🐂👃的呢?有兴趣就看我的下一片文章。最后大家不要忘了,如果我的文章对你有帮助,你愿意请我喝杯茶的话,请用支付宝扫一扫下面的二维码随意打赏。祝大家玩得愉快!


相关文章

  • OC中的类是怎么来的?

    博客地址:张飞的技术博客我们都知道OC是在C语言的基础上增加了面向对象的编程特性。我们知道C语言并没有面向对象中的...

  • OC中的类

    第一个OC类 1.如何声明一个类 注意: 必须以@interface开头,@end结尾 成员变量的声明,必须写在@...

  • Objective-C 中类的数据结构

    一、类的结构 OC 中的代码在底层实现,使用的是 C、C++,所以要研究 OC 中的类结构,可以将 OC 的代码转...

  • 深入理解Objective-C中类的数据结构

    一、类的结构 OC 中的代码在底层实现,使用的是 C、C++,所以要研究 OC 中的类结构,可以将 OC 的代码转...

  • 类方法02

    1、Oc 中的类 是以 “+” 号开头的。 2、类方法,只能由类 (名)来调用。 3、类方法中不能访问成员变量(也...

  • Objective-C day5 集合(17-07-31)

    OC中的集合包括:数组类 字典类 集合类##数组:1.数组是一个有序的集合,OC中的数组只能存储对象类型,但是...

  • Swift入门基础6——扩展、泛型、协议

    扩展 Swift中的扩展,对应OC中的分类,可以用它来拓展类的功能。 可以扩展类的什么: 添加新的属性(只能是计算...

  • 熟练使用Swift中的区间

    Swift中的区间类型对应着OC的NSRange类型,那么Swift中的区间类型是怎么使用的呢? 下面是了解区间类...

  • Runtime基础类型介绍

    Class:类Instance:实例 OC中的对象的实例本质上是 OC中的类本质上是 isa_t的结构是 这是一个...

  • OC对象的本质

    一、OC对象的底层实现 OC 中的代码在底层实现,使用的是 C、C++,所以要研究 OC 中的类结构,可以将 OC...

网友评论

    本文标题:OC中的类是怎么来的?

    本文链接:https://www.haomeiwen.com/subject/uduhhttx.html