欢迎关注
最酷最in的云资讯

SEL IMP Method解读

我们知道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实现/************************************************************************ class_getInstanceMethod.  Return the instance method for the* specified class and selector.**********************************************************************/Method class_getInstanceMethod(Class cls, SEL sel){    if (!cls  ||  !sel) return nil;        // This deliberately avoids +initialize because it historically did so.        // This implementation is a bit weird because it's the only place that     // wants a Method instead of an IMP.    #warning fixme build and search caches            // Search method lists, try method resolver, etc.    lookUpImpOrNil(cls, sel, nil,                    NO/*initialize*/, NO/*cache*/, YES/*resolver*/);                   #warning fixme build and search caches    return _class_getMethod(cls, sel);}

这里面调用了_class_getMethod()私有函数:

/************************************************************************ _class_getMethod* fixme* Locking: read-locks runtimeLock**********************************************************************/static Method _class_getMethod(Class cls, SEL sel){    mutex_locker_t lock(runtimeLock);    return getMethod_nolock(cls, sel);}/************************************************************************ getMethod_nolock* fixme* Locking: runtimeLock must be read- or write-locked by the caller**********************************************************************/static method_t *getMethod_nolock(Class cls, SEL sel){    method_t *m = nil;        runtimeLock.assertLocked();        // fixme nil cls?    // fixme nil sel?        assert(cls->isRealized());        while (cls  &&  ((m = getMethodNoSuper_nolock(cls, sel))) == nil) {        cls = cls->superclass;    }        return m;}

这个函数内部调用的是getMethodNoSuper_nolock():

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;}

查找方法的过程是先从本class的方法列表中查看是否存在,不存再在看父类递归这个过程( cls = cls->superclass).这个我们在以后讲消息发送,转发时在细看.

根据SEL获取类Method指针

OBJC_EXPORT Method _Nullableclass_getClassMethod(Class _Nullable cls, SEL _Nonnull name)    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

类对象的方法列表存放在元类中,所以获取类方法要去元类中查找,其在获取的时候参数已经指明元类:

/************************************************************************ class_getClassMethod.  Return the class method for the specified* class and selector.**********************************************************************/Method class_getClassMethod(Class cls, SEL sel){    if (!cls  ||  !sel) return nil;    //这里cls获取的是元类    return class_getInstanceMethod(cls->getMeta(), sel);}

返回一个函数的实现指针:

OBJC_EXPORT IMP _Nullableclass_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name)     OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

其实现过程:

IMP class_getMethodImplementation(Class cls, SEL sel){    IMP imp;        if (!cls  ||  !sel) return nil;        imp = lookUpImpOrNil(cls, sel, nil,                          YES/*initialize*/, YES/*cache*/, YES/*resolver*/);                             // Translate forwarding function to C-callable external version    if (!imp) {        return _objc_msgForward;    }        return imp;}

其内部我们可以看到两个主要的调用函数:lookUpImpOrNil()和_objc_msgForward.前面函数是获取SEL对应的IMP,其实现过程如下:

/************************************************************************ lookUpImpOrNil.* Like lookUpImpOrForward, but returns nil instead of _objc_msgForward_impcache**********************************************************************/IMP lookUpImpOrNil(Class cls, SEL sel, id inst,                    bool initialize, bool cache, bool resolver){    IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);    if (imp == _objc_msgForward_impcache) return nil;    else return imp;}// MARK: - 获取imp,先从缓存中查找imp,如果存在直接返回imp./************************************************************************ lookUpImpOrForward.* The standard IMP lookup. * initialize==NO tries to avoid +initialize (but sometimes fails)* cache==NO skips optimistic unlocked lookup (but uses cache elsewhere)* Most callers should use initialize==YES and cache==YES.* inst is an instance of cls or a subclass thereof, or nil if none is known. *   If cls is an un-initialized metaclass then a non-nil inst is faster.* May return _objc_msgForward_impcache. IMPs destined for external use *   must be converted to _objc_msgForward or _objc_msgForward_stret.*   If you don't want forwarding at all, use lookUpImpOrNil() instead.**********************************************************************/IMP lookUpImpOrForward(Class cls, SEL sel, id inst,                        bool initialize, bool cache, bool resolver){    IMP imp = nil;    bool triedResolver = NO;        runtimeLock.assertUnlocked();        // Optimistic cache lookup    if (cache) {        imp = cache_getImp(cls, sel);        if (imp) return imp;    }        // runtimeLock is held during isRealized and isInitialized checking    // to prevent races against concurrent realization.        // runtimeLock is held during method search to make    // method-lookup + cache-fill atomic with respect to method addition.    // Otherwise, a category could be added but ignored indefinitely because    // the cache was re-filled with the old value after the cache flush on    // behalf of the category.        runtimeLock.lock();    checkIsKnownClass(cls);        if (!cls->isRealized()) {        realizeClass(cls);    }        if (initialize  &&  !cls->isInitialized()) {        runtimeLock.unlock();        _class_initialize (_class_getNonMetaClass(cls, inst));        runtimeLock.lock();        // If sel == initialize, _class_initialize will send +initialize and         // then the messenger will send +initialize again after this         // procedure finishes. Of course, if this is not being called         // from the messenger then it won't happen. 2778172    }     retry:        runtimeLock.assertLocked();        // 从缓存中尝试查找IMP    // Try this class's cache.        imp = cache_getImp(cls, sel);    if (imp) goto done;        // 从本类的方法列表中尝试查找IMP    // Try this class's method lists.    {        Method meth = getMethodNoSuper_nolock(cls, sel);        if (meth) {            log_and_fill_cache(cls, meth->imp, sel, inst, cls);            imp = meth->imp;            goto done;        }    }        // 从父类的方法列表中尝试查找IMP    // Try superclass caches and method lists.    {        unsigned attempts = unreasonableClassCount();        for (Class curClass = cls->superclass;             curClass != nil;             curClass = curClass->superclass)        {            // Halt if there is a cycle in the superclass chain.            if (--attempts == 0) {                _objc_fatal("Memory corruption in class list.");            }                        // Superclass cache.            imp = cache_getImp(curClass, sel);            if (imp) {                if (imp != (IMP)_objc_msgForward_impcache) {                    // Found the method in a superclass. Cache it in this class.                    log_and_fill_cache(cls, imp, sel, inst, curClass);                    goto done;                }                else {                    // Found a forward:: entry in a superclass.                    // Stop searching, but don't cache yet; call method                     // resolver for this class first.                    break;                }            }                        // Superclass method list.            Method meth = getMethodNoSuper_nolock(curClass, sel);            if (meth) {                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);                imp = meth->imp;                goto done;            }        }    }        // 如果以上过程都没有找到,尝试一次动态方法解析    // No implementation found. Try method resolver once    .    if (resolver  &&  !triedResolver) {        runtimeLock.unlock();        _class_resolveMethod(cls, sel, inst);        runtimeLock.lock();        // Don't cache the result; we don't hold the lock so it may have         // changed already. Re-do the search from scratch instead.        triedResolver = YES;        goto retry;    }        // 如果方法解析也没有IMP,启动消息转发    // No implementation found, and method resolver didn't help.     // Use forwarding.        imp = (IMP)_objc_msgForward_impcache;    cache_fill(cls, sel, imp, inst);     done:    runtimeLock.unlock();        return imp;}

根据SEL在缓存中如果没有找到IMP,则在本类的Method获取找IMP,如果没有找到去父类中查找.以上过程都没有找到IMP的话,启动一次方法解析,方法解析也没有IMP的话就启动消息转发.

一个对象是否响应某个方法

// MARK: - 一个实例对象是否响应某个方法/**  * Returns a Boolean value that indicates whether instances of a class respond to a particular selector. *  * @param cls The class you want to inspect. * @param sel A selector. *  * @return \c YES if instances of the class respond to the selector, otherwise \c NO. *  * @note You should usually use \c NSObject's \c respondsToSelector: or \c instancesRespondToSelector:  *  methods instead of this function. */OBJC_EXPORT BOOLclass_respondsToSelector(Class _Nullable cls, SEL _Nonnull sel)     OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);    // MARK: - 是否响应某个方法BOOL class_respondsToSelector(Class cls, SEL sel){    return class_respondsToSelector_inst(cls, sel, nil);}// MARK: - 是否响应某个方法,其内部是通过获取IMP是否存在来判断// inst is an instance of cls or a subclass thereof, or nil if none is known.// Non-nil inst is faster in some cases. See lookUpImpOrForward() for details.bool class_respondsToSelector_inst(Class cls, SEL sel, id inst){    IMP imp;        if (!sel  ||  !cls) return NO;        // Avoids +initialize because it historically did so.    // We're not returning a callable IMP anyway.    imp = lookUpImpOrNil(cls, sel, inst,                          NO/*initialize*/, YES/*cache*/, YES/*resolver*/);    return bool(imp);}

其最终还是痛SEL是否有对应IMP来判断对象能否响应某个方法.

获取一个类的方法列表

// MARK: - 获取一个类的方法列表/**  * Describes the instance methods implemented by a class. *  * @param cls The class you want to inspect. * @param outCount On return, contains the length of the returned array.  *  If outCount is NULL, the length is not returned. *  * @return An array of pointers of type Method describing the instance methods  *  implemented by the class—any instance methods implemented by superclasses are not included.  *  The array contains *outCount pointers followed by a NULL terminator. You must free the array with free(). *  *  If cls implements no instance methods, or cls is Nil, returns NULL and *outCount is 0. *  * @note To get the class methods of a class, use \c class_copyMethodList(object_getClass(cls), &count). * @note To get the implementations of methods that may be implemented by superclasses,  *  use \c class_getInstanceMethod or \c class_getClassMethod. */OBJC_EXPORT Method _Nonnull * _Nullableclass_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount)     OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);// MARK: - 获取一个类所有实现的方法列表/************************************************************************ class_copyMethodList* fixme* Locking: read-locks runtimeLock**********************************************************************/Method *class_copyMethodList(Class cls, unsigned int *outCount){    unsigned int count = 0;    Method *result = nil;    if (!cls) {        if (outCount) *outCount = 0;        return nil;    }    mutex_locker_t lock(runtimeLock);        assert(cls->isRealized());    count = cls->data()->methods.count();    if (count > 0) {        result = (Method *)malloc((count + 1) * sizeof(Method));                count = 0;        for (auto& meth : cls->data()->methods) {            result[count++] = &meth;        }        result[count] = nil;    }        if (outCount) *outCount = count;    return result;}

获取一个方法名称

// MARK: - 获取一个方法名称/**  * Returns the name of a method. *  * @param m The method to inspect. *  * @return A pointer of type SEL. *  * @note To get the method name as a C string, call \c sel_getName(method_getName(method)). */OBJC_EXPORT SEL _Nonnullmethod_getName(Method _Nonnull m)     OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);    // MARK: - 获取一个方法名称/************************************************************************ method_getName* Returns this method's selector.* The method must not be nil.* The method must already have been fixed-up.* Locking: none**********************************************************************/SEL method_getName(Method m){    if (!m) return nil;        assert(m->name == sel_registerName(sel_getName(m->name)));    return m->name;}

获取一个方法名字最终还是通过sel_registerName()来获取,也是我们最开始的部分sel_init()提到.

获取一个方法的IMP

// MARK: - 获取一个方法的实现/**  * Returns the implementation of a method. *  * @param m The method to inspect. *  * @return A function pointer of type IMP. */OBJC_EXPORT IMP _Nonnullmethod_getImplementation(Method _Nonnull m)     OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);// MARK: - 获取一个方法的IMPIMP method_getImplementation(Method m){    return m ? m->imp : nil;}

通过直接Method中的imp成员变量

获取一个方法的参数和返回值类型

// MARK: - 获取一个方法的参数和返回值类型/**  * Returns a string describing a method's parameter and return types. *  * @param m The method to inspect. *  * @return A C string. The string may be \c NULL. */OBJC_EXPORT const char * _Nullablemethod_getTypeEncoding(Method _Nonnull m)     OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);    // MARK: - 获取一个方法的参数和返回值类型/************************************************************************ method_getTypeEncoding* Returns this method's old-style type encoding string.* The method must not be nil.* Locking: none**********************************************************************/const char *method_getTypeEncoding(Method m){    if (!m) return nil;    return m->types;}

获取一个方法的参数数量

// MARK: - 获取一个方法参数数量/**  * Returns the number of arguments accepted by a method. *  * @param m A pointer to a \c Method data structure. Pass the method in question. *  * @return An integer containing the number of arguments accepted by the given method. */OBJC_EXPORT unsigned intmethod_getNumberOfArguments(Method _Nonnull m)    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);    // MARK: - 获取一个方法的参数数量/************************************************************************ method_getNumberOfArguments.**********************************************************************/unsigned int method_getNumberOfArguments(Method m){    if (!m) return 0;    return encoding_getNumberOfArguments(method_getTypeEncoding(m));}

获取一个方法的返回值类型

// MARK: - 获取一个方法的返回值类型/**  * Returns a string describing a method's return type. *  * @param m The method to inspect. *  * @return A C string describing the return type. You must free the string with \c free(). */OBJC_EXPORT char * _Nonnullmethod_copyReturnType(Method _Nonnull m)     OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);    // MARK: - 获取方法的返回值类型char * method_copyReturnType(Method m){    return encoding_copyReturnType(method_getTypeEncoding(m));}

获取方法某个参数的类型

// MARK: - 获取方法某个参数的类型/**  * Returns a string describing a single parameter type of a method. *  * @param m The method to inspect. * @param index The index of the parameter to inspect. *  * @return A C string describing the type of the parameter at index \e index, or \c NULL *  if method has no parameter index \e index. You must free the string with \c free(). */OBJC_EXPORT char * _Nullablemethod_copyArgumentType(Method _Nonnull m, unsigned int index)     OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);    // MARK: - 获取方法某个参数类型char * method_copyArgumentType(Method m, unsigned int index){    return encoding_copyArgumentType(method_getTypeEncoding(m), index);}

获取方法返回值类型

// MARK: - 获取方法返回值类型/**  * Returns by reference a string describing a method's return type. *  * @param m The method you want to inquire about.  * @param dst The reference string to store the description. * @param dst_len The maximum number of characters that can be stored in \e dst. * * @note The method's return type string is copied to \e dst. *  \e dst is filled as if \c strncpy(dst, parameter_type, dst_len) were called. */OBJC_EXPORT voidmethod_getReturnType(Method _Nonnull m, char * _Nonnull dst, size_t dst_len)     OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);    // MARK: - 获取方法返回值类型void method_getReturnType(Method m, char *dst, size_t dst_len){    encoding_getReturnType(method_getTypeEncoding(m), dst, dst_len);}

更新设置某个方法的IMP

// MARK: - 更新设置某个方法的IMP/**  * Sets the implementation of a method. *  * @param m The method for which to set an implementation. * @param imp The implemention to set to this method. *  * @return The previous implementation of the method. */OBJC_EXPORT IMP _Nonnullmethod_setImplementation(Method _Nonnull m, IMP _Nonnull imp)     OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);    // MARK: - 更新设置某个方法IMPIMP method_setImplementation(Method m, IMP imp){    // Don't know the class - will be slow if RR/AWZ are affected    // fixme build list of classes whose Methods are known externally?    mutex_locker_t lock(runtimeLock);    return _method_setImplementation(Nil, m, imp);}// MARK: - 更新设置某个方法的IMP/************************************************************************ method_setImplementation* Sets this method's implementation to imp.* The previous implementation is returned.**********************************************************************/static IMP _method_setImplementation(Class cls, method_t *m, IMP imp){    runtimeLock.assertLocked();    if (!m) return nil;    if (!imp) return nil;    IMP old = m->imp;    m->imp = imp;    // Cache updates are slow if cls is nil (i.e. unknown)    // RR/AWZ updates are slow if cls is nil (i.e. unknown)    // fixme build list of classes whose Methods are known externally?    flushCaches(cls);    updateCustomRR_AWZ(cls, m);    return old;}

先找到old imp,然后把old imp 替换成 new imp.

交换两个方法的实现即交换两个方法的IMP

// MARK: - 交换两个方法的实现即交换两个方法的IMP/**  * Exchanges the implementations of two methods. *  * @param m1 Method to exchange with second method. * @param m2 Method to exchange with first method. *  * @note This is an atomic version of the following: *  \code  *  IMP imp1 = method_getImplementation(m1); *  IMP imp2 = method_getImplementation(m2); *  method_setImplementation(m1, imp2); *  method_setImplementation(m2, imp1); *  \endcode */OBJC_EXPORT voidmethod_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)     OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);    // MARK: - 交换两个方法的实现即交换两个方法的IMPvoid method_exchangeImplementations(Method m1, Method m2){    if (!m1  ||  !m2) return;        mutex_locker_t lock(runtimeLock);        IMP m1_imp = m1->imp;    m1->imp = m2->imp;    m2->imp = m1_imp;        // RR/AWZ updates are slow because class is unknown    // Cache updates are slow because class is unknown    // fixme build list of classes whose Methods are known externally?        flushCaches(nil);        updateCustomRR_AWZ(nil, m1);    updateCustomRR_AWZ(nil, m2);}

NSObject.h文件

执行某个方法

执行某个方法,一般用于方法解析中动态添加方法,下面这几个方法功能类似:

// MARK: - 执行某个方法 一般用于方法解析中动态添加方法- (id)performSelector:(SEL)aSelector;- (id)performSelector:(SEL)aSelector withObject:(id)object;- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;// MARK: -  执行某个方法 一般用于方法解析中动态添加方法- (id)performSelector:(SEL)sel {    if (!sel) [self doesNotRecognizeSelector:sel];    return ((id(*)(id, SEL))objc_msgSend)(self, sel);}- (id)performSelector:(SEL)sel withObject:(id)obj {    if (!sel) [self doesNotRecognizeSelector:sel];    return ((id(*)(id, SEL, id))objc_msgSend)(self, sel, obj);}- (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 {    if (!sel) [self doesNotRecognizeSelector:sel];    return ((id(*)(id, SEL, id, id))objc_msgSend)(self, sel, obj1, obj2);}

先判断这个方法是否存在doesNotRecognizeSelector(),不存在报错”unrecognized selector sent to instance”.然后调用了message.h中的消息发送函数.

内省方法:是否响应某个方法

// MARK: - 内省方法:是否响应某个方法- (BOOL)respondsToSelector:(SEL)aSelector;// MARK: - 是否响应某个方法- (BOOL)respondsToSelector:(SEL)sel {    if (!sel) return NO;    return class_respondsToSelector_inst([self class], sel, self);}// MARK: - 是否响应某个方法,其内部是通过获取IMP是否存在来判断// inst is an instance of cls or a subclass thereof, or nil if none is known.// Non-nil inst is faster in some cases. See lookUpImpOrForward() for details.bool class_respondsToSelector_inst(Class cls, SEL sel, id inst){    IMP imp;        if (!sel  ||  !cls) return NO;        // Avoids +initialize because it historically did so.    // We're not returning a callable IMP anyway.    imp = lookUpImpOrNil(cls, sel, inst,                          NO/*initialize*/, YES/*cache*/, YES/*resolver*/);    return bool(imp);}

一个实例对象是否响应某个方法

// MARK: - 一个对象是否响应某个方法+ (BOOL)instancesRespondToSelector:(SEL)aSelector;// MARK: - 一个对象是否响应某个方法+ (BOOL)instancesRespondToSelector:(SEL)sel {    if (!sel) return NO;    return class_respondsToSelector(self, sel);}

获取一个SEL对应的IMP

// MARK: - 获取一个SEL对应的IMP- (IMP)methodForSelector:(SEL)aSelector;// MARK: - 获取一个SEL对应的IMP- (IMP)methodForSelector:(SEL)sel {    if (!sel) [self doesNotRecognizeSelector:sel];    return object_getMethodImplementation(self, sel);}

获取一个实例方法SEL对应的IMP

// MARK: - 获取一个实例方法SEL对应的IMP+ (IMP)instanceMethodForSelector:(SEL)aSelector;// MARK: - 获取一个实例方法SEL对应的IMP+ (IMP)instanceMethodForSelector:(SEL)sel {    if (!sel) [self doesNotRecognizeSelector:sel];    return class_getMethodImplementation(self, sel);}

不能响应某个SEL

// MARK: - 不能响应某个SEL,报错- (void)doesNotRecognizeSelector:(SEL)aSelector;// MARK: - 不能响应某个方法// Replaced by CF (throws an NSException)- (void)doesNotRecognizeSelector:(SEL)sel {    _objc_fatal("-[%s %s]: unrecognized selector sent to instance %p",                 object_getClassName(self), sel_getName(sel), self);}

动态方法解析

动态方法解析,一般用来给某个没有实现的方法添加一个实现;返回false时,执行消息转发流程

// MARK: - 动态方法解析+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);// MARK: - 动态方法解析+ (BOOL)resolveClassMethod:(SEL)sel {    return NO;}+ (BOOL)resolveInstanceMethod:(SEL)sel {    return NO;}

消息转发相关

// MARK: - 消息转发相关- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");

本文主要介绍了SEL,IMP,Method的定义和实现,以及系统为我们提供的常用API.这里面涉及到到向一个对象发送消息的流程及转发过程,这个会在以后的章节中讲到.

编译后的源码放在Github,里面有相关阅读注释.

作者:偶尔登南山

链接:https://www.swifty.cc/p/d59056e49dfe

赞(0) 打赏
未经允许不得转载:云微资讯 » SEL IMP Method解读
分享到: 更多 (0)

云微资讯 科技新媒体资讯平台

关于我们联系我们

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏