iOS开发之深入理解runtimeRuntime源码iOS

iOS开发之runtime(20):sel_init()分析

2019-02-13  本文已影响15人  kyson老师

本系列博客是本人的源码阅读笔记,如果有iOS开发者在看runtime的,欢迎大家多多交流。为了方便讨论,本人新建了一个微信群(iOS技术讨论群),想要加入的,请添加本人微信:zhujinhui207407,【加我前请备注:ios 】,本人博客http://www.kyson.cn 也在不停的更新中,欢迎一起讨论

runtime logo

本文完整版详见笔者小专栏:https://xiaozhuanlan.com/runtime

背景

sel_init()的调用栈如下

-| _objc_init()
   -| _dyld_objc_notify_register
     -| map_images_nolock()
       -| sel_init()

其代码如下:

/***********************************************************************
* sel_init
* Initialize selector tables and register selectors used internally.
**********************************************************************/
void sel_init(size_t selrefCount)
{
    // save this value for later
    SelrefCount = selrefCount;

    builtins = preoptimizedSelectors();
    if (PrintPreopt  &&  builtins) {
        uint32_t occupied = builtins->occupied;
        uint32_t capacity = builtins->capacity;
        
        _objc_inform("PREOPTIMIZATION: using selopt at %p", builtins);
        _objc_inform("PREOPTIMIZATION: %u selectors", occupied);
        _objc_inform("PREOPTIMIZATION: %u/%u (%u%%) hash table occupancy",
                     occupied, capacity,
                     (unsigned)(occupied/(double)capacity*100));
    }

#define s(x) SEL_##x = sel_registerNameNoLock(#x, NO)
#define t(x,y) SEL_##y = sel_registerNameNoLock(#x, NO)

    sel_lock();
    
    s(load);
    s(initialize);
    t(resolveInstanceMethod:, resolveInstanceMethod);    
    t(resolveClassMethod:, resolveClassMethod);
    t(.cxx_construct, cxx_construct);
    t(.cxx_destruct, cxx_destruct);
    s(retain);
    s(release);
    s(autorelease);
    s(retainCount);
    s(alloc);
    t(allocWithZone:, allocWithZone);
    s(dealloc);
    s(copy);
    s(new);
    t(forwardInvocation:, forwardInvocation);
    t(_tryRetain, tryRetain);
    t(_isDeallocating, isDeallocating);
    s(retainWeakReference);
    s(allowsWeakReference);

    sel_unlock();

#undef s
#undef t
}

本文就带读者分析一下其源代码。

分析

宏的用法,以上的代码中有一个宏:

#define s(x) SEL_##x = sel_registerNameNoLock(#x, NO)

可能比较少人会了解###的用法。

#表示:对应变量字符串化
##表示:把宏参数名与宏定义代码序列中的标识符连接在一起,形成一个新的标识符

因此s(load);会被替换成:

SEL_load = sel_registerNameNoLock("load",NO);

t(resolveInstanceMethod:, resolveInstanceMethod);

会被改写成:

SEL_resolveInstanceMethod = sel_registerNameNoLock("resolveInstanceMethod:",NO);

我们进入sel_registerNameNoLock方法的定义中看:

SEL sel_registerNameNoLock(const char *name, bool copy) {
    return __sel_registerName(name, 0, copy);  // NO lock, maybe copy
}

__sel_registerName的定义如下:

static SEL __sel_registerName(const char *name, int lock, int copy) 
{
    SEL result = 0;

    if (lock) selLock.assertUnlocked();
    else selLock.assertWriting();

    if (!name) return (SEL)0;

    result = search_builtins(name);
    if (result) return result;
    
    if (lock) selLock.read();
    if (namedSelectors) {
        result = (SEL)NXMapGet(namedSelectors, name);
    }
    if (lock) selLock.unlockRead();
    if (result) return result;

    // No match. Insert.

    if (lock) selLock.write();

    if (!namedSelectors) {
        namedSelectors = NXCreateMapTable(NXStrValueMapPrototype, 
                                          (unsigned)SelrefCount);
    }
    if (lock) {
        // Rescan in case it was added while we dropped the lock
        result = (SEL)NXMapGet(namedSelectors, name);
    }
    if (!result) {
        result = sel_alloc(name, copy);
        // fixme choose a better container (hash not map for starters)
        NXMapInsert(namedSelectors, sel_getName(result), result);
    }

    if (lock) selLock.unlockWrite();
    return result;
}

可以发现,这个方法其实是讲selector添加到MapTable中,关于MapTable这里先不做介绍了,后面的文章会给出详细分析。
需要注意的是,被注册的selector会被放到一个静态变量namedSelectors中:

static NXMapTable *namedSelectors;

至此sel_init分析完成。

上一篇下一篇

猜你喜欢

热点阅读