我们知道iOS程序的入口函数在main.其实mian只是苹果给我们的”直观能够感受”的入口,在执行main之前,编译器已经帮我们做了相当多的事情.具体可以参考objc-os.h文件.Objective-C的Runtime库也是在main之前创建好的.我们关注sel_init()
SEL:
/************************************************************************ _objc_init* Bootstrap initialization. Registers our image notifier with dyld.* Called by libSystem BEFORE library initialization time**********************************************************************/void _objc_init(void){ static bool initialized = false; if (initialized) return; initialized = true; // fixme defer initialization until an objc-using image is found? environ_init(); tls_init(); static_init(); lock_init(); exception_init(); _dyld_objc_notify_register(&map_images, load_images, unmap_image);}
接口我们可以到sel_init()调用栈:
_| _objc_init() _| _dyld_objc_notify_register _| map_images_nolock() _| sel_init()
在map_images_nolock()方法中我们看到在sel_init()下面有arr_init():
void arr_init(void) { AutoreleasePoolPage::init(); SideTableInit();}
这个函数就是我们熟悉的AutoreleasePoolPage的初始化和全局SideTable的初始化,这个以后再分析.这里我们看一下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; #if SUPPORT_PREOPT 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)); }#endif // Register selectors used by libobjc #define s(x) SEL_##x = sel_registerNameNoLock(#x, NO)#define t(x,y) SEL_##y = sel_registerNameNoLock(#x, NO) mutex_locker_t lock(selLock); 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); #undef s#undef t}
我们可以看到一些常见系统内置方法分别调用__sel_registerName()这个方法:
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); if (namedSelectors) { result = (SEL)NXMapGet(namedSelectors, name); } if (result) return result; // No match. Insert. if (!namedSelectors) { namedSelectors = NXCreateMapTable(NXStrValueMapPrototype, (unsigned)SelrefCount); } if (!result) { // 初始化sel_alloc result = sel_alloc(name, copy); // 将selector 插入到NXMapTable 表中 // fixme choose a better container (hash not map for starters) NXMapInsert(namedSelectors, sel_getName(result), result); } return result;}
这方法的作用是将selector注册到NXMapTable表中,如果selector不存在则调用selector初始化方法,然后将selector作为selector作为key,selector作为value存到NXMapTable哈希表中:
static SEL sel_alloc(const char *name, bool copy){ selLock.assertLocked(); return (SEL)(copy ? strdupIfMutable(name) : name); }
在objc.h文件中我们可以看到SEL的声明:
/// An opaque type that represents a method selector.typedef struct objc_selector *SEL;
至此我们可以理解了,SEL就是一个表示方法的selector指针,映射方法的名字.
IMP:
/// A pointer to the function of a method implementation. #if !OBJC_OLD_DISPATCH_PROTOTYPEStypedef void (*IMP)(void /* id, SEL, ... */ ); #elsetypedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); #endif
从定义来看,IMP是一个指向实现函数的指针.IMP也是实现函数的入口,其和SEL的关系等以后将消息发送在细说.
Method:
method的声明结构:
typedef struct method_t *Method;
继续查看method_t的定义:
struct method_t { SEL name; const char *types; MethodListIMP imp; struct SortBySELAddress : public std::binary_function { bool operator() (const method_t& lhs, const method_t& rhs) { return lhs.name < rhs.name; } };};
method_t中有两个我们熟悉的成员变量:SEL和MethodListIMP,看一下MethodListIMP:
#if __has_feature(ptrauth_calls)// Method lists use process-independent signature for compatibility.// Method caches use process-dependent signature for extra protection.// (fixme not yet __ptrauth(...) because of `stp` inline asm in objc-cache.mm)using MethodListIMP = IMP __ptrauth_objc_method_list_imp;using MethodCacheIMP = StorageSignedFunctionPointer; #elseusing MethodListIMP = IMP;using MethodCacheIMP = IMP;#endif
MethodListIMP其实就是IMP,method可以理解为SEL(方法名称)和IMP(方法实现)相互对应的集合体.正常的情况一个SEL对应一个IMP,而SEL和IMP的绑定到运行时才确定的.
相关API
下面是NSObject.h和runtime.h文件为我们提供的有关SEL,IMP和Method相关的接口及说明:
runtim.h文件:
根据SEL获取实例Method指针
// 获取Method声明OBJC_EXPORT Method _Nullableclass_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name) OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0); // 获取Method实现/****************************