OC底层相关iOS

iOS - 回顾Objective-C的对象模型

2019-05-07  本文已影响20人  Longshihua

Objective-C对象模型

Objective-CRuntime是开源的,可以在这里下载,在objc4-532.2以后,苹果把NSObject的实现也开源了

isa 指针

Objective-C是一门面向对象的编程语言。每一个对象都是一个类的实例。Objective-C语言的内部,每一个对象都有一个名为isa的指针,指向该对象的类。

打开刚刚下载的源码,在Object.h文件中可以看到Object类,该类拥有isa成员,类型为Class

@interface Object
{
    Class isa;  /* A pointer to the instance's class structure */
}

同样在objc.h文件中找到Class,并且可以看到 Objective-C中的对象的定义struct objc_object,是的,Objective-C 中的对象本质上是结构体对象,其中 isa 是它唯一的私有成员变量。 如下所示

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

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

由上可知,Classobjc_class类型的指针,并且还有看到我们熟悉的id指针,因为idstruct objc_object类型的指针,所以id类型可以指向任意对象。

下面看看objc_class结构体,在objc-class.h文件中可以找到定义

/* 
 *  Class Template
 */
struct objc_class {         
    struct objc_class *isa; 
    struct objc_class *super_class; 
    const char *name;       
    long version;
    long info;
    long instance_size;
    struct objc_ivar_list *ivars;

#if defined(Release3CompatibilityBuild)
    struct objc_method_list *methods;
#else
    struct objc_method_list **methodLists;
#endif

    struct objc_cache *cache;
    struct objc_protocol_list *protocols;
};

可以看到objc_class是类模版,即类的定义为struct objc_class,所以 Objective-C中类也是一个结构体对象。

struct objc_class类的定义,我们可以了解很多东西:

每一个类描述了一系列它的实例的特点,包括:成员变量的列表,成员函数的列表,缓存,协议等。每一个对象都可以接受消息,而对象能够接收的消息列表是保存在它所对应的类中。

元类

Objective-C 语言中,每一个类实际上也是一个对象。每一个类也有一个名为 isa 的指针。每一个类也可以接受消息,例如:[NSObject alloc],就是向NSObject这个类发送名为alloc消息。

因为类也是一个对象,那它也必须是另一个类的实列,这个类就是元类 (metaclass)。元类保存了类方法的列表。当一个类方法被调用时,元类会首先查找它本身是否有该类方法的实现,如果没有,则该元类会向它的父类查找该方法,直到一直找到继承链的头。

元类 (metaclass) 也是一个对象,那么元类的 isa 指针又指向哪里呢?所有的元类的isa指针都会指向一个根元类 (root metaclass)。根元类 (root metaclass) 本身的 isa 指针指向自己,这样就行成了一个闭环。

isa 和继承的关系如下图

class.png

可以从图中看出:

类的成员变量

如果把类的实例看成一个C语言的结构体(struct),上面说的isa 指针就是这个结构体的第一个成员变量,而类的其它成员变量依次排列在结构体中

struct objc_class {         
    struct objc_class *isa; 
    struct objc_class *super_class; 
    const char *name;       
    long version;
    long info;
    long instance_size;
    struct objc_ivar_list *ivars;

#if defined(Release3CompatibilityBuild)
    struct objc_method_list *methods;
#else
    struct objc_method_list **methodLists;
#endif

    struct objc_cache *cache;
    struct objc_protocol_list *protocols;
};

从上面的Objective-C对象模型中可以看到,除了isa指针外还有很多其他成员,比如标记父类的super_class , 表示类名的nameversion为一个long类型, 应该表示这个对象的版本号,instance_size 应该表示这个类对象大概在内存中占用的大小等。

对象中的ivars是指向成员属性列表的指针:

/* 
 *  Instance Variable Template
 */
typedef struct objc_ivar *Ivar;

struct objc_ivar_list {
    int ivar_count;
#ifdef __alpha__
    int space;
#endif
    struct objc_ivar {
        char *ivar_name;
        char *ivar_type;
        int ivar_offset;
#ifdef __alpha__
        int space;
#endif
    } ivar_list[1];         /* variable length structure */
};

有数量标记ivar_count 、空间标记 space 以及ojbc_ivar

对象中的methodList是指向实例变量列表的指针

typedef struct objc_method *Method;

struct objc_method_list {
#if defined(Release3CompatibilityBuild)
        struct objc_method_list *method_next;
#else
    struct objc_method_list *obsolete;
#endif

    int method_count;
#ifdef __alpha__
    int space;
#endif
    struct objc_method {
        SEL method_name;
        char *method_types;
        IMP method_imp;
    } method_list[1];       /* variable length structure */
};

obsolete根据名字来猜,指向废弃的函数列表,method_count是函数个数,space为空间大小,然后objc_method中有函数选择器method_name, 描述类型(包含参数类型以及返回值类型)的字符串method_types以及函数指针method_imp

对象中的cache对象类型为objc_cache的结构体

typedef struct objc_cache * Cache;

struct objc_cache {
    unsigned int mask;            /* total = mask + 1 */
    unsigned int occupied;        
    Method buckets[1];
};

Cache为方法调用的性能进行优化,通俗地讲,每当实例对象接收到一个消息时,它不会直接在 isa指向的类的方法列表中遍历查找能够响应消息的方法,因为这样效率太低了,而是优先在 Cache中查找。 Runtime系统会把被调用的方法存到Cache中(理论上讲一个方法如果被调用,那么它有可能今后还会被调用),这样下次查找的时候效率更高。

对象中的protocols表示遵循的协议列表

@class Protocol;

struct objc_protocol_list {
    struct objc_protocol_list *next;
    int count;
    Protocol *list[1];
};

指向下一个协议列表的指针next ,表示协议个数的count以及协议列表 list[1],

可变与不可变

因为对象在内存中的排布可以看成一个结构体,该结构体的大小并不能动态变化。所以无法在运行时动态给对象增加成员变量。

相对的,对象的方法定义都保存在类的可变区域中。方法的定义列表是一个名为methodLists的指针的指针(如下图所示)。通过修改该指针指向的指针的值,就可以实现动态地为某一个类增加成员方法。这也是Category实现的原理。同时也说明了为什么Category只可为对象增加成员方法,却不能增加成员变量。

屏幕快照 2019-05-07 下午3.10.10.png

注意,通过objc_setAssociatedObjectobjc_getAssociatedObject方法可以变相地给对象增加成员变量,但由于实现机制不一样,所以并不是真正改变了对象的内存结构。

除了对象的方法可以动态修改,因为isa本身也只是一个指针,所以我们也可以在运行时动态地修改isa指针的值,达到替换对象整个行为的目的。不过该应用场景较少。

参考

Objective-C对象模型及应用
Objective-C 对象模型
Objective-C 中的类和对象

上一篇 下一篇

猜你喜欢

热点阅读