Fuck iOS EveryDayiOS开发之底层

解读objc源码:重新认识类和对象

2018-06-07  本文已影响38人  ElaineYin
  1. 为什么说类也是对象?
  2. 为什么不能在运行时添加成员变量?
  3. 为什么可以动态添加方法?

个人觉得,带着问题看答案,应该会理解更深一些,OK,开讲啦!!!

1、认识真正的类:objc_class

1.1 我们眼中的Class

苹果开放给我们的objc库中对类和对象是这么定义的:

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

/// 对象:类的实例
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
/// 类结构体
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;
    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;

从上面的代码我们看到类实例(对象)中只有一个元素,isa指针,这个指针指向的是实例所述的类,类结构体中也有一个isa指针,其他的我好像是看不出来了,现在我们从objc源代码看一下类真正的结构

1.2 源码中的类定义

objc_class
代码摘自objc4-723版本,代码太长,所以只看下属性

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    class_rw_t *data() { 
        return bits.data();
    }
}

跟我们之前看到的类定义对比一下:

只有这几个属性,我们的方法列表、属性列表、变量什么的哪去了?我觉得应该跟data()有关,一看bits就是存数据的,data()是class_rw_t类型的,应该能从class_rw_t定义里面找到原因,来看下class_rw_t:


class_rw_t

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;

#if SUPPORT_INDEXED_ISA
    uint32_t index;
#endif
}

看到了我们熟悉的方法列表、属性列表、协议列表,又看到了一个跟class_rw_t一样的东东class_ro_t,继续深入:


class_ro_t

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

终于看到了我们正在寻找的成员变量列表,再看看,里面竟然还有baseMethodList、baseProtocols、baseProperties这些列表,看到这我忽然有个猜测:

光有猜测也行不通,怎么验证一下呢,想到这里(其实是想了好久),我忽然有了个办法,我们可以用Runtime的几个copy列表的方法看一下呀
class_copyIvarList:获取变量列表,如果我们猜测没有错误的话,返回的应该是类的class_rw_t里面的class_ro_t下面的ivarlist
class_copyMethodList:我们猜测因为runtime可以动态添加方法、协议,那么返回的应该是class_rw_t下面的methods,而不是class_ro_t里面的,因为ro是const类型的。
OK,来看下在objc源码中它的实现(看下关键代码就行):

Ivar *
class_copyIvarList(Class cls, unsigned int *outCount)
{
    ......

    if ((ivars = cls->data()->ro->ivars)  &&  ivars->count) {
        result = (Ivar *)malloc((ivars->count+1) * sizeof(Ivar));
        
        for (auto& ivar : *ivars) {
            if (!ivar.offset) continue;  // anonymous bitfield
            result[count++] = &ivar;
        }
        result[count] = nil;
    }
    
    if (outCount) *outCount = count;
    return result;
}

Method *
class_copyMethodList(Class cls, unsigned int *outCount)
{
    ......
    count = cls->data()->methods.count();

    if (count > 0) {
        result = (Method *)malloc((count + 1) * sizeof(Method));
        
        count = 0;
        for (auto& meth : cls->data()->methods) {
            result[count++] = &meth;
        }
        result[count] = nil;
    }

    if (outCount) *outCount = count;
    return result;
}

没错,跟猜测的一样一样的,其他代码就不贴了哈,有兴趣的同学自己看看。

这样一看,很多我们之前不理解的都能说通了:

感觉终于理解啦,以后别人再问我为什么不能添加变量的时候,我就能妥妥的回答啦~~ 心情好愉悦 ~~

上一篇 下一篇

猜你喜欢

热点阅读