美文网首页Objective-c
iOS进阶回顾三「msgSend」

iOS进阶回顾三「msgSend」

作者: Coder东 | 来源:发表于2019-08-27 22:46 被阅读0次
  • OC中的方法的调用,其实都是转换为objc_msgSend函数的调用
  • 继续调用进阶回顾二SFPerson对象,我们可以理解为personrun方法调用最终在c语言的如下方法调用:
  objc_msgSend(person, @selector(run));
  消息接收者(receiver):person
  消息名称:run
  • objc_msgSend的执行流程:消息发送 -> 动态解析 -> 消息转发
    查看源码
ENTRY _objc_msgSend
UNWIND _objc_msgSend, NoFrame
// 寄存器:消息接受者 receiver
cmp    p0, #0            // nil check and tagged pointer check
#if SUPPORT_TAGGED_POINTERS
b.le    LNilOrTagged        //  (MSB tagged pointer looks negative)
#else
b.eq    LReturnZero
#endif
ldr    p13, [x0]        // p13 = isa
GetClassFromIsa_p16 p13        // p16 = class
LGetIsaDone:
CacheLookup NORMAL        // calls imp or objc_msgSend_uncached
// 查找缓存
#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
b.eq    LReturnZero        // nil check

// tagged
adrp    x10, _objc_debug_taggedpointer_classes@PAGE
add    x10, x10, _objc_debug_taggedpointer_classes@PAGEOFF
ubfx    x11, x0, #60, #4
ldr    x16, [x10, x11, LSL #3]
adrp    x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGE
add    x10, x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGEOFF
cmp    x10, x16
b.ne    LGetIsaDone

// ext tagged
adrp    x10, _objc_debug_taggedpointer_ext_classes@PAGE
add    x10, x10, _objc_debug_taggedpointer_ext_classes@PAGEOFF
ubfx    x11, x0, #52, #8
ldr    x16, [x10, x11, LSL #3]
b    LGetIsaDone
// SUPPORT_TAGGED_POINTERS
#endif

LReturnZero:
// x0 is already zero
mov    x1, #0
movi    d0, #0
movi    d1, #0
movi    d2, #0
movi    d3, #0
ret  相当于C语言的return

END_ENTRY _objc_msgSend
  • 通过CacheLookup NORMAL方法进行缓存中查找,如果没有找到则从STATIC_ENTRY __objc_msgSend_uncached方法查找MethodTableLookup查找_class_lookupMethodAndCache3进行查找,然后查找IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver)有点绕了
    3633CBC2E1B7A08BC498A0172CADD441.png
  • 查看方法是否在当前类对象的缓存中,如果没有就进行查找调用Method meth = getMethodNoSuper_nolock(cls, sel);方法查找
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
    runtimeLock.assertLocked();

    assert(cls->isRealized());
    // fixme nil cls? 
    // fixme nil sel?

    for (auto mlists = cls->data()->methods.beginLists(), 
              end = cls->data()->methods.endLists(); 
         mlists != end;
         ++mlists)
    {
        method_t *m = search_method_list(*mlists, sel);
        if (m) return m;
    }

    return nil;
}

该方法又使用method_t *m = search_method_list(*mlists, sel);进行查找 发现search_method

static method_t *search_method_list(const method_list_t *mlist, SEL sel)
{
    int methodListIsFixedUp = mlist->isFixedUp();
    int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
    
    if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
        return findMethodInSortedMethodList(sel, mlist);
    } else {
        // Linear search of unsorted method list
        for (auto& meth : *mlist) {
            if (meth.name == sel) return &meth;
        }
    }

#if DEBUG
    // sanity-check negative results
    if (mlist->isFixedUp()) {
        for (auto& meth : *mlist) {
            if (meth.name == sel) {
                _objc_fatal("linear search worked when binary search did not");
            }
        }
    }
#endif

    return nil;
}

再查找到 sorted方法

static method_t *findMethodInSortedMethodList(SEL key, const method_list_t *list)
{
    assert(list);

    const method_t * const first = &list->first;
    const method_t *base = first;
    const method_t *probe;
    uintptr_t keyValue = (uintptr_t)key;
    uint32_t count;
    
    for (count = list->count; count != 0; count >>= 1) {
        probe = base + (count >> 1);
        
        uintptr_t probeValue = (uintptr_t)probe->name;
        
        if (keyValue == probeValue) {
            // `probe` is a match.
            // Rewind looking for the *first* occurrence of this value.
            // This is required for correct category overrides.
            while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
                probe--;
            }
            return (method_t *)probe;
        }
        
        if (keyValue > probeValue) {
            base = probe + 1;
            count--;
        }
    }
    
    return nil;
}

分别使用二分法线性查找进行查找

  • 消息转发流程图如下:


    228F997CCF8C839E11374E1D38CE0FA9.png
  • 总结:首先判断消息接受者receiver是否为nil,如果为nil直接退出,如果不为nil则从当前接受者类对象receiverClass的缓存cache中查找方法,如果查找到就调用,如果没有查找到,去当前接受者类对象的class_rw_t中查找方法,找到调用,没找到去父类的缓存cache中查找,如果找到调用,没找到去父类的class_rw_t中查找方法,找到调用,没找到就行动态方法解析

相关文章

网友评论

    本文标题:iOS进阶回顾三「msgSend」

    本文链接:https://www.haomeiwen.com/subject/xgyjectx.html