runtime分析记录

runtime

isa

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

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
      uintptr_t nonpointer        : 1;                                       \
      uintptr_t has_assoc         : 1;                                       \
      uintptr_t has_cxx_dtor      : 1;                                       \
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
      uintptr_t magic             : 6;                                       \
      uintptr_t weakly_referenced : 1;                                       \
      uintptr_t deallocating      : 1;                                       \
      uintptr_t has_sidetable_rc  : 1;                                       \
      uintptr_t extra_rc          : 19
    };
#endif
};
复制代码
  • nonpointer: 表示是否对 isa 指针开启指针优化 。 0,表明普通的指针,存储着
  • Class、Meta-Class对象的内存地址 1,表明优化过,使用位域存储更多的信息
  • has_assoc: 是否有设置过关联对象,0没有,1存在,若是没有,释放时会更快,
  • has_cxx_dtor: 是否有C++的析构函数(.cxx_destruct),若是有析构函数,则须要作析构逻辑, 若是没有,则能够更快的释放对象。
  • shiftcls: 存储着Class、Meta-Class对象的内存地址信息,在 arm64 架构中有 33 位用来存储类指针。因此类对象、元类对象的地址最后3位都是0
  • magic: 用于在调试时分辨对象是否未完成初始化
  • weakly_referenced: 是否有被弱引用指向过,若是没有,释放时会更快
  • deallocating: 对象是否正在释放
  • has_sidetable_rc: 对象引用计数是否大于 10,大于10 时,则须要借用sideTable进行存储
  • extra_rc: 里面存储的值是引用计数器减1,表示该对象的引用计数值,其实是引用计数值减 1, 例如,若是对象的引用计数为 10,那么 extra_rc 为 9。若是引用计数大于 10, 则须要使用到下面的 has_sidetable_rc。

class结构:

class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容数组

cache_t 方法缓存

sstruct cache_t {
    explicit_atomic<struct bucket_t *> _buckets; //散列表
    explicit_atomic<mask_t> _mask; //散列表长度-1

#if __LP64__
    uint16_t _flags;
#endif
    uint16_t _occupied; //已经缓存的方法数量

public:
    static bucket_t *emptyBuckets();
    
    struct bucket_t *buckets();
    mask_t mask();
    mask_t occupied();
    void incrementOccupied();
    void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
    void initializeToEmpty();

    unsigned capacity();
    bool isConstantEmptyCache();
    bool canBeFreed();

#if __LP64__
    bool getBit(uint16_t flags) const ;
    void setBit(uint16_t set) ;
    void clearBit(uint16_t clear) ;
#endif

#if FAST_CACHE_ALLOC_MASK
    bool hasFastInstanceSize(size_t extra) const;

    size_t fastInstanceSize(size_t extra) const;

    void setFastInstanceSize(size_t newSize);
#else
    bool hasFastInstanceSize(size_t extra) const {
        return false;
    }
    size_t fastInstanceSize(size_t extra) const {
        abort();
    }
    void setFastInstanceSize(size_t extra) {
        // nothing
    }
#endif

    static size_t bytesForCapacity(uint32_t cap);
    static struct bucket_t * endMarker(struct bucket_t *b, uint32_t cap);

    void reallocate(mask_t oldCapacity, mask_t newCapacity, bool freeOld);
    void insert(Class cls, SEL sel, IMP imp, id receiver);

    static void bad_cache(id receiver, SEL sel, Class isa) __attribute__((noreturn, cold));
};


复制代码
struct bucket_t {
private:
    explicit_atomic<uintptr_t> _imp;
    explicit_atomic<SEL> _sel;

    uintptr_t modifierForSEL(SEL newSel, Class cls) const {
        return (uintptr_t)&_imp ^ (uintptr_t)newSel ^ (uintptr_t)cls;
    }
modifiers.
    uintptr_t encodeImp(IMP newImp, SEL newSel, Class cls) const {
        if (!newImp) return 0;
		return (uintptr_t)newImp ^ (uintptr_t)cls;
    }

public:
    inline SEL sel() const { return _sel.load(memory_order::memory_order_relaxed); }

    inline IMP imp(Class cls) const {
        uintptr_t imp = _imp.load(memory_order::memory_order_relaxed);
        if (!imp) return nil;
		return (IMP)(imp ^ (uintptr_t)cls);
    }

    template <Atomicity, IMPEncoding>
    void set(SEL newSel, IMP newImp, Class cls);
};
复制代码
  • 存入
void cache_t::insert(Class cls, SEL sel, IMP imp, id receiver)
{
    runtimeLock.assertLocked();

    ASSERT(sel != 0 && cls->isInitialized());

        if (slowpath(isConstantEmptyCache())) {
        // Cache is read-only. Replace it.
        if (!capacity) capacity = INIT_CACHE_SIZE;
        reallocate(oldCapacity, capacity, /* freeOld */false);
    }
    else if (fastpath(newOccupied <= capacity / 4 * 3)) {
        // Cache is less than 3/4 full. Use it as-is.
    }
    else {
        capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;
        if (capacity > MAX_CACHE_SIZE) {
            capacity = MAX_CACHE_SIZE;
        }
        //空间不够则空间*2开辟
        reallocate(oldCapacity, capacity, true);
    }

    bucket_t *b = buckets();
    mask_t m = capacity - 1;
    mask_t begin = cache_hash(sel, m);
    mask_t i = begin;

    do {
        if (fastpath(b[i].sel() == 0)) {
            //找到空的插入,而后cache_t的_occupied++
            incrementOccupied();
            b[i].set<Atomic, Encoded>(sel, imp, cls);
            return;
        }
        if (b[i].sel() == sel) {
            //已经存在直接返回
            return;
        }
    } while (fastpath((i = cache_next(i, m)) != begin));
    //没有位置报错
    cache_t::bad_cache(receiver, (SEL)sel, cls);
}

复制代码

存入的位置是用bucket_t_imp&mask,mask=散列表长度-1,源码以下缓存

static inline mask_t cache_hash(SEL sel, mask_t mask) 
{
    return (mask_t)(uintptr_t)sel & mask;
}
复制代码

若是算出的位置是被占用了,则会顺序-1一直到空的地方,最多循环一圈,源码以下:markdown

static inline mask_t cache_next(mask_t i, mask_t mask) {
    return i ? i-1 : mask;
}
复制代码

method_t

typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 

struct method_t {
    SEL name; //名称
    const char *types; //编码值(返回值、参数)
    MethodListIMP imp; //函数地址
};
复制代码
  1. SEL表明方法\函数名,通常叫作选择器,底层结构跟char *相似
  2. 能够经过@selector()和sel_registerName()得到
  3. 能够经过sel_getName()和NSStringFromSelector()转成字符串
  4. 不一样类中相同名字的方法,所对应的方法选择器是相同的
  5. types包含了函数返回值、参数编码的字符串

iOS中提供了一个叫作@encode的指令,能够将具体的类型表示成字符串编码 架构


objc_msgSend

objc_msgSend的执行流程能够分为3大阶段less

  1. 消息发送
  2. 动态方法解析
  3. 消息转发

源码分析

  • 消息发送

该阶段会先去isa指向的类的cache中寻找方法,若是没有找到则会去该类的rw中的methods中寻找,若该类没有则像该类的superclass中寻找,寻找步骤依旧如上述所说,最后若是没有找到则会来到动态解析(resove)。ide

ENTRY _objc_msgSend
	
	cbz	r0, LNilReceiver_f //若ro(self)为0则跳转LNilReceiver

	ldr	r9, [r0]		// r9 = self->isa
	GetClassFromIsa			// r9 = class
	CacheLookup NORMAL, _objc_msgSend //缓存寻找方法
	// cache hit, IMP in r12, eq already set for nonstret forwarding
	bx	r12			// call imp //直接跳转r12地址 即imp

	CacheLookup2 NORMAL, _objc_msgSend 
	// cache miss
	ldr	r9, [r0]		// r9 = self->isa
	GetClassFromIsa			// r9 = class
	b	__objc_msgSend_uncached

LNilReceiver:
	// r0 is already zero
	mov	r1, #0
	mov	r2, #0
	mov	r3, #0
	FP_RETURN_ZERO
	bx	lr	        //等价于 mov pc,lr,即跳转返回

END_ENTRY _objc_msgSend
复制代码

cache中找不到则来到 __objc_msgSend_uncached函数

STATIC_ENTRY __objc_msgSend_uncached

	// THIS IS NOT A CALLABLE C FUNCTION
	// Out-of-band r9 is the class to search

	MethodTableLookup NORMAL	// returns IMP in r12 搜索方法列表
	bx	r12

END_ENTRY __objc_msgSend_uncached
复制代码

该方法调用MethodTableLookup去搜索源码分析

.macro MethodTableLookup
	
	stmfd	sp!, {r0-r3,r7,lr}
	add	r7, sp, #16
	sub	sp, #8			// align stack
	FP_SAVE

	// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
.if $0 == NORMAL
	// receiver already in r0
	// selector already in r1
.else
	mov 	r0, r1			// receiver
	mov 	r1, r2			// selector
.endif
	mov	r2, r9			// class to search
	mov	r3, #3			// LOOKUP_INITIALIZE | LOOKUP_INITIALIZE
	blx	_lookUpImpOrForward 
	mov	r12, r0			// r12 = IMP
	
.if $0 == NORMAL
	cmp	r12, r12		// set eq for nonstret forwarding
.else
	tst	r12, r12		// set ne for stret forwarding
.endif

	FP_RESTORE
	add	sp, #8			// align stack
	ldmfd	sp!, {r0-r3,r7,lr}

.endmacro
复制代码
  • 动态解析

该方法其实会去调用_lookUpImpOrForward去寻找imp,若是imp不存在则会返回forward的imp优化

IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior/*内含标记位,根据标记位去寻找方法*/)
{
    const IMP forward_imp = (IMP)_objc_msgForward_impcache; //消息转发
    IMP imp = nil;
    Class curClass;

    runtimeLock.assertUnlocked();

    // Optimistic cache lookup
    if (fastpath(behavior & LOOKUP_CACHE)) { 
        imp = cache_getImp(cls, sel);
        if (imp) goto done_nolock;
    }


    runtimeLock.lock();

    
    checkIsKnownClass(cls);
    //初始化cls,包含rw等内容
    if (slowpath(!cls->isRealized())) {
        cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
    }

    if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
        cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
       
    }

    runtimeLock.assertLocked();
    curClass = cls;

    
    //循环遍历cls以及父类方法列表
    for (unsigned attempts = unreasonableClassCount();;) { 
        // curClass method list.
        Method meth = getMethodNoSuper_nolock(curClass, sel); //遍历class的rw的methods
        if (meth) {
            imp = meth->imp;
            goto done;
        }
        //子类没有则获取父类继续寻找
        if (slowpath((curClass = curClass->superclass) == nil)) { 
            //一直没有找到实现则赋值forward_imp
            imp = forward_imp;
            break;
        }

    
        // huan cun
        imp = cache_getImp(curClass, sel);
        if (slowpath(imp == forward_imp)) {
            // Found a forward:: entry in a superclass.
            // Stop searching, but don't cache yet; call method
            // resolver for this class first.
            break;
        }
        if (fastpath(imp)) {
            // Found the method in a superclass. Cache it in this class.
            goto done;
        }
    }

    // 没有发现实现,则试着去调用resolver方法,而且只会调用一次
    if (slowpath(behavior & LOOKUP_RESOLVER)) {
        behavior ^= LOOKUP_RESOLVER;
        return resolveMethod_locked(inst, sel, cls, behavior);
    }

 done:
    log_and_fill_cache(cls, imp, sel, inst, curClass); //找到则缓存到clas本身的缓存中,不管方法是本类或父类的
    runtimeLock.unlock();
 done_nolock:
    if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
        return nil;
    }
    return imp;
}
复制代码

lookUpImpOrForward中会尝试调用cache_getImp再去找一下imp,找到则会跳转到done_nolock返回,若是类没有初始化则会去初始化类,包含rw等内容,下面就会循环遍历cls以及父类方法列表,若是找到则跳转done而后将方法缓存到该类的cache中,若是遍历完了都没有找到,则imp则会被赋值fordward,若是尚未尝试过调用resolver方法,而且只会调用一次,保证调用一次的缘由是采用behavior的位值,关键代码behavior ^= LOOKUP_RESOLVER,下面就是调用resolve的内容(元类和类的2种方法)ui

static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    SEL resolve_sel = @selector(resolveInstanceMethod:);

    if (!lookUpImpOrNil(cls, resolve_sel, cls->ISA())) {
        // 没有实现Resolver则直接返回
        return;
    }

    //调用Resolver方法,在方法中咱们能够动态添加方法实现
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, resolve_sel, sel); //这里能够看出Resolver返回yes或者no都没有影响,结果只是用来打印了

    //再次寻找sel实现,若是Resolver方法中,咱们已经动态添加了,则会把实现缓存下来
    IMP imp = lookUpImpOrNil(inst, sel, cls);

    if (resolved  &&  PrintResolving) {
        if (imp) {
            _objc_inform("RESOLVE: method %c[%s %s] "
                         "dynamically resolved to %p", 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel), imp);
        }
        else {
            // Method resolver didn't add anything?
            _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
                         ", but no new implementation of %c[%s %s] was found",
                         cls->nameForLogging(), sel_getName(sel), 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel));
        }
    }
}
static void resolveClassMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    ASSERT(cls->isMetaClass());

    if (!lookUpImpOrNil(inst, @selector(resolveClassMethod:), cls)) {
        // Resolver not implemented.
        return;
    }

    Class nonmeta;
    {
        mutex_locker_t lock(runtimeLock);
        nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
        // +initialize path should have realized nonmeta already
        if (!nonmeta->isRealized()) {
            _objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
                        nonmeta->nameForLogging(), nonmeta);
        }
    }
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);

    // Cache the result (good or bad) so the resolver doesn't fire next time.
    // +resolveClassMethod adds to self->ISA() a.k.a. cls
    IMP imp = lookUpImpOrNil(inst, sel, cls);

    if (resolved  &&  PrintResolving) {
        if (imp) {
            _objc_inform("RESOLVE: method %c[%s %s] "
                         "dynamically resolved to %p", 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel), imp);
        }
        else {
            // Method resolver didn't add anything?
            _objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
                         ", but no new implementation of %c[%s %s] was found",
                         cls->nameForLogging(), sel_getName(sel), 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel));
        }
    }
}

复制代码

没有实现resolver方法则直接返回,调用resolver方法,在方法中咱们能够动态添加方法实现,在代码中能够看出resolver方法返回yes或者no都没有影响,调用完了以后,会再次寻找sel实现,若是rsolver方法中,咱们已经动态添加了,则会把实现缓存下来。

  • 消息转发

最后若是都没有发现,则会进入消息转发,若是咱们没有实现forward方法则会报错方法找不到。 下方是流程图

相关文章
相关标签/搜索