本系列博客是本人的源码阅读笔记,如果有 iOS 开发者在看 runtime 的,欢迎大家多多交流。为了方便讨论,本人新建了一个微信群(iOS技术讨论群),想要加入的,请添加本人微信:zhujinhui207407,【加我前请备注:ios 】,本人博客http://www.kyson.cn 也在不停的更新中,欢迎一起讨论
本文完整版详见笔者小专栏: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
分析完成。
广告
我的首款个人开发的APP壁纸宝贝上线了,欢迎大家下载。