Objective-C类和方法底层初探 (二)

443 阅读4分钟

其他文章请点击下方:

  1. Objective-C对象底层初探 (一)
  2. 应用加载过程初探 (三)

实例对象、类对象、元类之间的关系

类对象只会在内存里存在一份

    XLPerson *p = [[XLPerson alloc] init];//实例对象
    
    Class class1= [XLPerson class]; //类对象
    Class class2= p.class;//类对象
    Class class3= object_getClass(p);//类对象 
    NSLog(@"\n%p-\n%p-\n%p",class1,class2,class3); //结果是同一个指针
    
    Class class4= object_getClass(class3);  //元类

实例对象

由类初始化出来的对象就是实例对象。

类对象

通过元类实例化出来的对象就是类对象。

元类

类对象所属的类,系统自动实现。 元类的归属是根元类。

Isa

是一个联合体,为了节省内存消耗。 数据共享一块内存。 是存在我们的类里。

private:
    isa_t isa;
    
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

优化isa:普通的isa&ISA_MASK

经曲isa走位图:

注: 根元类的父类是NSOjbect,NSObject的父类是nil

类结构

struct objc_class : objc_object {
    // Class ISA;       // 8
    Class superclass;   // 8
    cache_t cache;      // 16       // 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();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }

    void setInfo(uint32_t set) {
        assert(isFuture()  ||  isRealized());
        data()->setFlags(set);
    }
    .....
}

执行 class_data_bits_t 结构体中的 data() 方法会返回同一个 class_rw_t * 指针,ObjC 类中的属性、方法还有遵循的协议等信息都保存在 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;

其中还有一个指向常量的指针 ro,其中存储了当前类在编译期就已经确定的属性、方法以及遵循的协议。

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;
    }
};

方法

实例方法获取

    Method method1 = class_getInstanceMethod([XLPerson class], @selector(xl_instanceMethod)); // 类对象拿实例方法
    Method method2 = class_getInstanceMethod(objc_getMetaClass("XLPerson"), @selector(xl_instanceMethod)); // 元类对象拿实例方法

    Method method3 = class_getInstanceMethod([XLPerson class], @selector(xl_classMethod));//类对象拿类方法
    Method method4 = class_getInstanceMethod(objc_getMetaClass("XLPerson"), @selector(xl_classMethod));//元类对象拿类方法
    NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
   

结果1和4有值,2和3没有值。 这也得出来一个结论:

  1. 类方法存在我们的元类中。
  2. 对象实例方法存在我们的类中。

方法实现获取

    IMP imp1 = class_getMethodImplementation([XLPerson class], @selector(xl_instanceMethod));
    IMP imp2 = class_getMethodImplementation([XLPerson class], @selector(xl_classMethod));
    IMP imp3 = class_getMethodImplementation(objc_getMetaClass("XLPerson"), @selector(xl_instanceMethod));
    IMP imp4 = class_getMethodImplementation(objc_getMetaClass("XLPerson"), @selector(xl_classMethod));
    NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);

结果都有值,但2和3通过系统进行消息转发,看下面源码:

IMP class_getMethodImplementation(Class cls, SEL sel)
{
    IMP imp;

    if (!cls  ||  !sel) return nil;

    imp = lookUpImpOrNil(cls, sel, nil, 
                         YES/*initialize*/, YES/*cache*/, YES/*resolver*/);

    // Translate forwarding function to C-callable external version
    if (!imp) {
        return _objc_msgForward;//消息转发
    }

    return imp;
}

类方法获取

    Method method1 = class_getClassMethod([XLPerson class], @selector(xl_instanceMethod));
    Method method2 = class_getClassMethod(objc_getMetaClass("XLPerson"), @selector(xl_instanceMethod));
    
    Method method3 = class_getClassMethod([XLPerson class], @selector(xl_classMethod));
    Method method4 = class_getClassMethod(objc_getMetaClass("XLPerson"), @selector(xl_classMethod));
    NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);

很显然,1和2是没有值的,但3和4是有结果,而且结果是一样的。这是两种获取类方法的写法,看源码实现:

/***********************************************************************
* class_getClassMethod.  Return the class method for the specified
* class and selector.
**********************************************************************/
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}

元类获取的类方法等价于类获取的类方法。

协议\属性\方法

method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
    class method_array_t : 
    public list_array_tt<method_t, method_list_t> 
{
    typedef list_array_tt<method_t, method_list_t> Super;

 public:
    method_list_t **beginCategoryMethodLists() {
        return beginLists();
    }
    
    method_list_t **endCategoryMethodLists(Class cls);

    method_array_t duplicate() {
        return Super::duplicate<method_array_t>();
    }
};


class property_array_t : 
    public list_array_tt<property_t, property_list_t> 
{
    typedef list_array_tt<property_t, property_list_t> Super;

 public:
    property_array_t duplicate() {
        return Super::duplicate<property_array_t>();
    }
};


class protocol_array_t : 
    public list_array_tt<protocol_ref_t, protocol_list_t> 
{
    typedef list_array_tt<protocol_ref_t, protocol_list_t> Super;

 public:
    protocol_array_t duplicate() {
        return Super::duplicate<protocol_array_t>();
    }
};

方法、协议、属性都是继承的list_array_tt,所以添加方法、协议、属性都是通过attachLists这个函数实现的。

 void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;

        if (hasArray()) {
            // many lists -> many lists
            uint32_t oldCount = array()->count;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
            array()->count = newCount;
            memmove(array()->lists + addedCount, array()->lists, 
                    oldCount * sizeof(array()->lists[0]));
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
        else if (!list  &&  addedCount == 1) {
            // 0 lists -> 1 list
            list = addedLists[0];
        } 
        else {
            // 1 list -> many lists
            List* oldList = list;
            uint32_t oldCount = oldList ? 1 : 0;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)malloc(array_t::byteSize(newCount)));
            array()->count = newCount;
            if (oldList) array()->lists[addedCount] = oldList;
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
    }

此方法会加载类、分类、添加属性、添加方法、添加协议的时候调用


/***********************************************************************
* methodizeClass
* Fixes up cls's method list, protocol list, and property list.
* Attaches any outstanding categories.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static void methodizeClass(Class cls)
{
    runtimeLock.assertLocked();

    bool isMeta = cls->isMetaClass();
    auto rw = cls->data();
    auto ro = rw->ro;

    // Methodizing for the first time
    if (PrintConnecting) {
        _objc_inform("CLASS: methodizing class '%s' %s", 
                     cls->nameForLogging(), isMeta ? "(meta)" : "");
    }

    // Install methods and properties that the class implements itself.
    method_list_t *list = ro->baseMethods();
    if (list) {
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
        rw->methods.attachLists(&list, 1);
    }

    property_list_t *proplist = ro->baseProperties;
    if (proplist) {
        rw->properties.attachLists(&proplist, 1);
    }

    protocol_list_t *protolist = ro->baseProtocols;
    if (protolist) {
        rw->protocols.attachLists(&protolist, 1);
    }

    // Root classes get bonus method implementations if they don't have 
    // them already. These apply before category replacements.
    if (cls->isRootMetaclass()) {
        // root metaclass
        addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
    }

    // Attach categories.
    category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
    attachCategories(cls, cats, false /*don't flush caches*/);

    if (PrintConnecting) {
        if (cats) {
            for (uint32_t i = 0; i < cats->count; i++) {
                _objc_inform("CLASS: attached category %c%s(%s)", 
                             isMeta ? '+' : '-', 
                             cls->nameForLogging(), cats->list[i].cat->name);
            }
        }
    }
    
    if (cats) free(cats);

#if DEBUG
    // Debug: sanity-check all SELs; log method list contents
    for (const auto& meth : rw->methods) {
        if (PrintConnecting) {
            _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(meth.name));
        }
        assert(sel_registerName(sel_getName(meth.name)) == meth.name); 
    }
#endif
}