iOS 面试题: runtime

objc_msgSend(object, selector)

做的事情,是通过 selector,去找方法实现 IMP , 指向函数的指针, 函数地址

从 selector, 到 class_rw_t 里面的 method_t ( Method )

method_t 里面:

Method 的实现

struct method_t {
    SEL name;// 函数名
    const char *types; // 编码,( 返回值类型,参数类型 ) 
    MethodListIMP imp;// 指向函数的指针 ( 函数地址 )

    struct SortBySELAddress :
        public std::binary_function<const method_t&,
                                    const method_t&, bool>
    {
        bool operator() (const method_t& lhs,
                         const method_t& rhs)
        { return lhs.name < rhs.name; }
    };
};

取方法分两种情况:
第一次取

class_rw_t 里面:

方法数组

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
    // 在方法数组里面找
   const method_array_t methods() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>()->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>()->baseMethods()};
        }
    }
 

method_array_t 里面,去找 Method

方法数组的实现

 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_array_t() : Super() { }
    method_array_t(method_list_t *l) : Super(l) { }

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

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

然后进入

struct method_t

以后取,走方法缓存

方法缓存 cache_t, 用散列表 ( 哈希表 ) 来缓存曾经调用过的方法,
可以提高方法的查找速度

cache_t 的实现

struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic<struct bucket_t *> _buckets; // 散列表
    explicit_atomic<mask_t> _mask;  // 散列表的长度 
    #if __LP64__
    uint16_t _flags;
#endif
    uint16_t _occupied;  // 已经缓存的方法数量

cache_t 中,找到 IMP

struct bucket_t {
private:
    // IMP-first is better for arm64e ptrauth and no worse for arm64.
    // SEL-first is better for armv7* and i386 and x86_64.
#if __arm64__
    explicit_atomic<uintptr_t> _imp;    // 函数的内存地址
    explicit_atomic<SEL> _sel; // SEL 作为 Key 
#else
    explicit_atomic<SEL> _sel;
    explicit_atomic<uintptr_t> _imp;
#endif

交换方法method_exchangeImplementations

+ (void)load{
    
    Method tap = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
    Method listen = class_getInstanceMethod(self, @selector(listenAction:to:forEvent:));
    method_exchangeImplementations(tap, listen);
    
    
}

这样搞一下,发生了什么?


原来

@selector(sendAction:to:forEvent:)

对应

 
Method: sendAction:to:forEvent: 的实现

另一边

@selector(listenAction:to:forEvent:)

对应

Method: listenAction:to:forEvent: 的实现

交换后:

@selector(sendAction:to:forEvent:)

对应

Method: listenAction:to:forEvent: 的实现

另一边

 
@selector(listenAction:to:forEvent:)

对应

Method: sendAction:to:forEvent: 的实现

交换方法:就是交换一下实现指针,并刷新了方法缓存

 void method_exchangeImplementations(Method m1, Method m2)
{
    if (!m1  ||  !m2) return;

    mutex_locker_t lock(runtimeLock);
	// 交换一下实现指针 IMP 
    IMP m1_imp = m1->imp;
    m1->imp = m2->imp;
    m2->imp = m1_imp;


    // RR/AWZ updates are slow because class is unknown
    // Cache updates are slow because class is unknown
    // fixme build list of classes whose Methods are known externally?
 	// 清空方法缓存
    flushCaches(nil);

    adjustCustomFlagsForMethodChange(nil, m1);
    adjustCustomFlagsForMethodChange(nil, m2);
}

清空缓存:

void _objc_flush_caches(Class cls)
{
    {
        mutex_locker_t lock(runtimeLock);
        flushCaches(cls);
        if (cls  &&  cls->superclass  &&  cls != cls->getIsa()) {
            flushCaches(cls->getIsa());
        } else {
            // cls is a root class or root metaclass. Its metaclass is itself
            // or a subclass so the metaclass caches were already flushed.
        }
    }

    if (!cls) {
        // collectALot if cls==nil
#if CONFIG_USE_CACHE_LOCK
        mutex_locker_t lock(cacheUpdateLock);
#else
        mutex_locker_t lock(runtimeLock);
#endif
        cache_collect(true);
    }
}
 

代码部分:

GitHub 链接

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值