iOS runtimeruntime

深入浅出 Runtime(二):数据结构

2019-11-16  本文已影响0人  师大小海腾

Runtime 系列文章

深入浅出 Runtime(一):初识
深入浅出 Runtime(二):数据结构
深入浅出 Runtime(三):消息机制
深入浅出 Runtime(四):super 的本质
深入浅出 Runtime(五):具体应用
深入浅出 Runtime(六):相关面试题

网络配图

目录

  • 1. objc_object
  • 2. objc_class
    2.1 class_data_bits_t
    2.2 cache_t
     2.2.1 缓存查找流程
     2.2.2 缓存添加流程
     2.2.3 缓存扩容流程
  • 3. isa指针
    3.1 isa 与 superclass 指针指向
    3.2 类对象(class)与元类对象(meta-class)
    3.3 获得 class 或者 meta-class 的方式
  • 4. method_t
    4.1 SEL
    4.2 IMP
    4.3 Type Encodings

1. objc_object

Objective-C的面向对象都是基于C/C++的数据结构——结构体实现的。
我们平时使用的所有对象都是id类型,id类型对象对应到runtime中,就是objc_object结构体。

// A pointer to an instance of a class.
typedef struct objc_object *id;
struct objc_object {
private:
    isa_t isa;
    /*...
      isa操作相关
      弱引用相关
      关联对象相关
      内存管理相关
      ...
     */
};

2. objc_class

Class指针用来指向一个 Objective-C 的类,它是objc_class结构体类型,所以class、meta-class底层结构都是objc_class结构体,objc_class继承自objc_object,所以它也有isa指针,它也是对象。

// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
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();
    }
};

2.1 class_data_bits_t

struct class_data_bits_t {
    // Values are the FAST_ flags above.
    uintptr_t bits;
public:
    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
};
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;
};
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;  // instance对象占用的内存空间
#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_array_t 与 method_list_t

2.2 cache_t

struct cache_t {
    struct bucket_t *_buckets;  // 哈希表
    mask_t _mask;               // 哈希表的长度 - 1
    mask_t _occupied;           // 已经缓存的方法数量
};

struct bucket_t {
private:
    cache_key_t _key;  // SEL
    IMP _imp;          // IMP 函数的内存地址
};

2.2.1 缓存查找流程

//objc-cache.mm(objc4)
bucket_t * cache_t::find(cache_key_t k, id receiver)  // 根据 k 即 @selector 进行查找
{
    assert(k != 0);

    bucket_t *b = buckets();          // 获取_buckets
    mask_t m = mask();                // 获取_mask
    mask_t begin = cache_hash(k, m);  // 计算起始索引
    mask_t i = begin;
    do {
        // 根据索引 i 从 _buckets 哈希表中取值
        // 如果取出来的 bucket_t 的 _key = 0,说明在索引的位置上还没有缓存过方法,返回该 bucket_t,中止缓存查询,用于 cache_fill_nolock() 函数
        // 如果取出来的 bucket_t 的 _key = k,说明查询成功,返回该 bucket_t
        if (b[i].key() == 0  ||  b[i].key() == k) {
            return &b[i];
        }
      // 在 __arm64__ 下将索引 i -1,继续查找,反向遍历 _buckets 哈希表
      // 直到 i 指向首个元素即索引 = 0 时,将 mask 赋值给 i,使其指向哈希表最后一个元素,继续反向遍历
      // 如果此时还没有找到 k 对应的 bucket_t ,或者是空的 bucket_t ,则循环结束,查找失败,调用 bad_cache() 函数
      // 接下来去类对象中 class_rw_t 中的 methods 查找
    } while ((i = cache_next(i, m)) != begin);

    // hack
    Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));
    cache_t::bad_cache(receiver, (SEL)k, cls);
}


static inline mask_t cache_hash(cache_key_t key, mask_t mask) 
{
    return (mask_t)(key & mask);
}
static inline mask_t cache_next(mask_t i, mask_t mask) {
    // return (i+1) & mask;  // __arm__  ||  __x86_64__  ||  __i386__
    return i ? i-1 : mask;   // __arm64__
}

2.2.2 缓存添加流程

//objc-cache.mm(objc4)
static void cache_fill_nolock(Class cls, SEL sel, IMP imp, id receiver)
{
    cacheUpdateLock.assertLocked();

    // Never cache before +initialize is done
    if (!cls->isInitialized()) return;   // 如果类还未初始化,直接返回

    // Make sure the entry wasn't added to the cache by some other thread 
    // before we grabbed the cacheUpdateLock.
    if (cache_getImp(cls, sel)) return;  // 可能有其它线程抢先将该方法缓存了,所以要检查一次缓存,如果存在,直接返回

    cache_t *cache = getCache(cls);  // ⚠️取出该 class 的 cache_t
    cache_key_t key = getKey(sel);   // ⚠️根据 sel 获得 _key

    // Use the cache as-is if it is less than 3/4 full
    mask_t newOccupied = cache->occupied() + 1;  // 将 cache_t 的 _occupied 即已经缓存的方法数量 + 1,这里只是为了判断 +1 后缓存容量是否满
    mask_t capacity = cache->capacity();  // 获得缓存容量 = _mask + 1
    if (cache->isConstantEmptyCache()) {  // 如果缓存是只读的,重新申请缓存空间
        // Cache is read-only. Replace it.
        cache->reallocate(capacity, capacity ?: INIT_CACHE_SIZE);  // 申请新的缓存空间,并释放旧的
    }
    else if (newOccupied <= capacity / 4 * 3) {  // ⚠️如果当前已经缓存的方法数量 +1 <= 缓存容量的 3/4,就继续往下操作
        // Cache is less than 3/4 full. Use it as-is.
    }
    else {  // ⚠️如果以上条件不满足,说明缓存已满,进行缓存扩容
        // Cache is too full. Expand it.
        cache->expand();
    }

    // Scan for the first unused slot and insert there.     // 扫描第一个未使用的插槽(bucket_t)并将其插入
    // There is guaranteed to be an empty slot because the  // 必然会有一个空的插槽(bucket_t)
    // minimum size is 4 and we resized at 3/4 full.        // 因为最小大小是4,我们调整为3/4满
    bucket_t *bucket = cache->find(key, receiver);       // ⚠️调用 find() 函数进行一次缓存查找,必然会得到一个空的 bucket_t
    if (bucket->key() == 0) cache->incrementOccupied();  // ⚠️如果该 bucket_t 为空,将 _occupied 即已经缓存的方法数量 + 1
    bucket->set(key, imp);  // ⚠️添加缓存
}

void cache_fill(Class cls, SEL sel, IMP imp, id receiver)
{
#if !DEBUG_TASK_THREADS
    mutex_locker_t lock(cacheUpdateLock);
    cache_fill_nolock(cls, sel, imp, receiver);
#else
    _collecting_in_critical();
    return;
#endif
}

2.2.3 缓存扩容流程

//objc-cache.mm(objc4)
void cache_t::expand()
{
    cacheUpdateLock.assertLocked();
    
    uint32_t oldCapacity = capacity();
    // ⚠️将缓存扩容为原来的两倍,如果是首次调用,设置缓存容量的初始值为 4
    uint32_t newCapacity = oldCapacity ? oldCapacity*2 : INIT_CACHE_SIZE;

    if ((uint32_t)(mask_t)newCapacity != newCapacity) {
        // mask overflow - can't grow further
        // fixme this wastes one bit of mask
        newCapacity = oldCapacity;
    }

    reallocate(oldCapacity, newCapacity);  // ⚠️申请新的缓存空间,并释放旧的
}

enum {
    INIT_CACHE_SIZE_LOG2 = 2,
    INIT_CACHE_SIZE      = (1 << INIT_CACHE_SIZE_LOG2)
};

void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity)
{
    bool freeOld = canBeFreed();  // ⚠️判断一下缓存是不是空的,如果为空,就没必要释放空间

    bucket_t *oldBuckets = buckets();
    bucket_t *newBuckets = allocateBuckets(newCapacity);

    // Cache's old contents are not propagated. 
    // This is thought to save cache memory at the cost of extra cache fills.
    // fixme re-measure this

    assert(newCapacity > 0);
    assert((uintptr_t)(mask_t)(newCapacity-1) == newCapacity-1);

    setBucketsAndMask(newBuckets, newCapacity - 1);
    
    if (freeOld) {
        cache_collect_free(oldBuckets, oldCapacity);
        cache_collect(false);
    }
}

bool cache_t::canBeFreed()
{
    return !isConstantEmptyCache();
}

bool cache_t::isConstantEmptyCache()
{
    return 
        occupied() == 0  &&  
        buckets() == emptyBucketsForCapacity(capacity(), false);
}

3. isa 指针

struct objc_object {
    Class isa;  // 在 arm64 架构之前
};

struct objc_object {
private:
    isa_t isa;  // 在 arm64 架构开始
};

union isa_t 
{
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;

#if SUPPORT_PACKED_ISA

    // extra_rc must be the MSB-most field (so it matches carry/overflow flags)
    // nonpointer must be the LSB (fixme or get rid of it)
    // shiftcls must occupy the same bits that a real class pointer would
    // bits + RC_ONE is equivalent to extra_rc + 1
    // RC_HALF is the high bit of extra_rc (i.e. half of its range)

    // future expansion:
    // uintptr_t fast_rr : 1;     // no r/r overrides
    // uintptr_t lock : 2;        // lock for atomic property, @synch
    // uintptr_t extraBytes : 1;  // allocated with extra bytes

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL  // 用来取出 Class、Meta-Class 对象的内存地址
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    struct {
        uintptr_t nonpointer        : 1;  // 0:代表普通的指针,存储着 Class、Meta-Class 对象的内存地址
                                          // 1:代表优化过,使用位域存储更多的信息
        uintptr_t has_assoc         : 1;  // 是否有设置过关联对象,如果没有,释放时会更快
        uintptr_t has_cxx_dtor      : 1;  // 是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
        uintptr_t shiftcls          : 33; // 存储着 Class、Meta-Class 对象的内存地址信息
        uintptr_t magic             : 6;  // 用于在调试时分辨对象是否未完成初始化
        uintptr_t weakly_referenced : 1;  // 是否有被弱引用指向过,如果没有,释放时会更快
        uintptr_t deallocating      : 1;  // 对象是否正在释放
        uintptr_t has_sidetable_rc  : 1;  // 如果为1,代表引用计数过大无法存储在 isa 中,那么超出的引用计数会存储在一个叫 SideTable 结构体的 RefCountMap(引用计数表)哈希表中
        uintptr_t extra_rc          : 19; // 里面存储的值是引用计数 retainCount - 1
#       define RC_ONE   (1ULL<<45)
#       define RC_HALF  (1ULL<<18)
    };
};

3.1 isa 与 superclass 指针指向

isa 与 superclass 指针指向

3.2 类对象(class)与元类对象(meta-class)

3.3 获得 class 或者 meta-class 的方式

- (Class)class;
+ (Class)class;
Class object_getClass(id obj);  // 传参:instance 对象
Class object_getClass(id obj);  // 传参:Class 对象

示例代码如下

    NSObject *object1 = [NSObject alloc] init];
    NSObject *object2 = [NSObject alloc] init];
    // objectClass1 ~ objectClass5 都是 NSObject 的类对象
    Class objectClass1 = [object1 class];
    Class objectClass2 = [object2 class];
    Class objectClass3 = [NSObject class];
    Class objectClass4 = object_getClass(object1);
    Class objectClass5 = object_getClass(object2);  
    // objectMetaClass1 ~ objectMetaClass4 都是 NSObject 的元类对象
    Class objectMetaClass1 = object_getClass([object1 class];    
    Class objectMetaClass2 = object_getClass([NSObject class]);    
    Class objectMetaClass3 = object_getClass(object_getClass(object1));    
    Class objectMetaClass4 = object_getClass(objectClass5);    

方法实现

- (Class)class {
    return object_getClass(self);
}

+ (Class)class {
    return self;
}

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}
objc_object::getIsa() 
{
    if (!isTaggedPointer()) return ISA();
    ......
}
objc_object::ISA() 
{
    assert(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK);
#endif
}
#if __ARM_ARCH_7K__ >= 2
#   define SUPPORT_INDEXED_ISA 1
#else
#   define SUPPORT_INDEXED_ISA 0
#endif

3.4 为什么要设计 meta-class ?

目的是将实例和类的相关方法列表以及构建信息区分开来,方便各司其职,符合单一职责设计原则。

4. method_t

typedef struct method_t *Method;
struct method_t {
    SEL name;  // 方法名
    const char *types;  // 编码(返回值类型、参数类型)
    IMP imp;   // 方法的地址/实现
};

4.1 SEL

typedef struct objc_selector *SEL;
    SEL sel1 = @selector(selector);
    SEL sel2 = sel_registerName("selector");
    SEL sel3 = NSSelectorFromString(@"selector");
    char *string1 = sel_getName(sel1);
    NSString *string2 = NSStringFromSelector(sel1);

4.2 IMP

#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
#endif

4.3 Type Encodings

Objective-C type encodings

 

下一篇

深入浅出 Runtime(三):消息机制

上一篇 下一篇

猜你喜欢

热点阅读