@selector() 与SEL 到底是啥
背景:
iOS 经常看到sdk中要传递个@selector,或者反射的时候也要这个,那这个到底是啥,今天我们从源码的角度来看下
测试代码
Objective-C @autoreleasepool { SEL sel = @selector(abcefg); } |
经过oc 转c++后看下到底是什么
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
C++ { __AtAutoreleasePool __autoreleasepool; SEL sel = sel_registerName("abcefg"); } |
也就是说@selector 转成c++ 后就变成了sel_registerName("abcefg");搞清楚这个东西sel_registerName ,也就找到了@selector 到底是啥了
sel_registerName
得益于objc4开源了,我们可以找到对应的源码
C++ SEL sel_registerName(const char *name) { return __sel_registerName(name, 1, 1); // YES lock, YES copy } static SEL __sel_registerName(const char *name, bool shouldLock, bool copy) { SEL result = 0; if (shouldLock) selLock.assertUnlocked(); else selLock.assertLocked(); if (!name) return (SEL)0; //这个是重点 result = search_builtins(name); if (result) return result; conditional_mutex_locker_t lock(selLock, shouldLock); auto it = namedSelectors.get().insert(name); if (it.second) { // No match. Insert. *it.first = (const char *)sel_alloc(name, copy); } return (SEL)*it.first; } static SEL search_builtins(const char *name) { #if SUPPORT_PREOPT if (SEL result = (SEL)_dyld_get_objc_selector(name)) return result; #endif return nil; } |
上边源码可以看的出来sel_registerName->__sel_registerName->search_builtins->_dyld_get_objc_selector,而_dyld_get_objc_selector这个函数在objc4源码里边并没有,而是在dyld源码里边。
另外_dyld_get_objc_selector返回是空,还会走下边插入到h
https://opensource.apple.com/source/dyld/通过这个源码可以看到_dyld_get_objc_selector的实现,接下来我们就看看它的源码
_dyld_get_objc_selector
C++ const char* _dyld_get_objc_selector(const char* selName) { log_apis("dyld_get_objc_selector()\n"); return gAllImages.getObjCSelector(selName); }
const char* AllImages::getObjCSelector(const char *selName) const { if ( _objcSelectorHashTable == nullptr ) return nullptr; return _objcSelectorHashTable->getString(selName, _objcSelectorHashTableImages.array()); } const char* ObjCStringTable::getString(const char* selName, const Array<uintptr_t>& baseAddresses) const { StringTarget target = getPotentialTarget(selName); if (target == sentinelTarget) return nullptr; dyld3::closure::Image::ObjCImageOffset imageAndOffset; imageAndOffset.raw = target; uintptr_t sectionBaseAddress = baseAddresses[imageAndOffset.imageIndex]; const char* value = (const char*)(sectionBaseAddress + imageAndOffset.imageOffset); if (!strcmp(selName, value)) return value; return nullptr; } |
通过这个返回值可以看出来SEL == const char* ,也就是oc方法去掉类型后的名字, 它会去找方法名字与dyld加载后的方法符号名字进行对比,如果是一样的就说明找到方法了,返回, 也就是@selector本质上就是找方法名字