zoukankan      html  css  js  c++  java
  • iOS 底层原理探索之 isKindOfClass & isMemberOfClass

    isMemberOfClass
    今天通过两个经典面试题,继续加深对isa & 继承关系 & 类结构 的理解。
    在之前的isa探索中,我们了解了:
    isa的指向:对象的 isa 指向 类; 类的 isa 指向 元类;元类的 isa 指向 根元类;根元类的 isa 指向 自己。
    类的superclass的指向:类的 superclass 指向 父类, 父类的 superclass 指向 根类 ,根类的superclass 指向 nil;
    元类的superclass的指向:元类的 superclass 指向 父类的元类,父元类的 superclass 指向 根类的元类 根元类的 superclass 指向 根类 根类的 superclass 指向 nil。

    示例1 iskindOfClass & isMemberOfClass

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            //iskindOfClass & isMemberOfClass 类方法调用
            BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];  
            BOOL re2 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];     
            BOOL re3 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];        
            BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];     
            NSLog(@" re1 :%hhd
     re2 :%hhd
     re3 :%hhd
     re4 :%hhd
    ",re1,re2,re3,re4);
            
            //iskindOfClass & isMemberOfClass 实例方法调用
            BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];        
            BOOL re6 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]; 
            BOOL re7 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];        
            BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];   
            NSLog(@" re5 :%hhd
     re6 :%hhd
     re7 :%hhd
     re8 :%hhd
    ",re5,re6,re7,re8);
        }
        return 0;
    }
    

    打印结果:

    re1 :1
    re2 :0
    re3 :0
    re4 :0
    
    re5 :1
    re6 :1
    re7 :1
    re8 :1
    

    在分析实例之前我们先来看一下源码解析

    源码解析

    isKindOfClass 源码解析(实例方法 & 类方法)

    //--isKindOfClass---类方法、对象方法
    //+ isKindOfClass:第一次比较是 获取类的元类 与 传入类对比,再次之后的对比是获取上次结果的父类 与 传入 类进行对比
    + (BOOL)isKindOfClass:(Class)cls {
        // 获取类的元类 vs 传入类
        // 根元类 vs 传入类
        // 根类 vs 传入类
        // 举例:LGPerson vs 元类 (根元类) (NSObject)
        for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
    
    //- isKindOfClass:第一次是获取对象类 与 传入类对比,如果不相等,后续对比是继续获取上次 类的父类 与传入类进行对比
    - (BOOL)isKindOfClass:(Class)cls {
    /*
    获取对象的类 vs 传入的类 
    父类 vs 传入的类
    根类 vs 传入的类
    nil vs 传入的类
    */
        for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
    
    

    isMemberOfClass 源码解析(实例方法 & 类方法)

    //类方法
    //+ isMemberOfClass : 获取类的元类,与 传入类对比
    + (BOOL)isMemberOfClass:(Class)cls {
        return self->ISA() == cls;
    }
    //实例方法
    //- isMemberOfClass : 获取对象的类,与 传入类对比
    - (BOOL)isMemberOfClass:(Class)cls {
        return [self class] == cls;
    }
    

    源码分析总结:
    isKindOfClass

    类方法:元类 --> 根元类 --> 根类 --> nil 与 传入类的对比
    实例方法:对象的类 --> 父类 --> 根类 --> nil 与 传入类的对比

    isMemberOfClass

    类方法: 类的元类传入类 对比
    实例方法:对象的父类传入类 对比

    透过上面的源码解析总结,我们分析我们的示例代码

    ret1 = 1 : 传入的cls 为 NSobject, self 指向 NSobject,进入循环
    第一次循环: tcls 为 NSobject meta ,cls 为 NSobject ;执行判断条件if (tcls == cls) ,不相等;执行 tcls = tcls->superclass ,此时 tcls 指向 NSobject meta 的父类 ,即 NSObject。进入第二次循环。
    第二次循环:此时 tcls 为 NSobject,cls 依然是 NSobject,执行判断条件 if (tcls == cls) 相等,return YES。
    所以 re1 的结果为 1。

    ret2 = 0 : 传入的cls为 NSobject,self指向 Person,进入循环
    第一次循环:tcls 为 Person meta,cls 为 Person类; 执行判断条件 if (tcls == cls) ,不相等,执行 tcls = tcls->superclass ,此时 tcls 指向 NSobject metal。进入第二次循环。
    第二次循环: tcls 为 NSobject meta ,cls 为 Person类;不相等,执行 tcls = tcls->superclass ,此时 tcls 指向 NSObject。进入第三次循环。
    第三次循环: tcls 为 NSobject ,cls 为 Person类;不相等,执行 tcls = tcls->superclass ,此时 tcls 指向 nil。不满足for循环执行条件 tcls。结束循环。
    所以 re2 的结果为 0。

    ret3 = 0 : 传入的cls 为 NSObject, self 指向 NSObject
    self->ISA( ) ,self的 isa 指向 NSObject meta ;NSObject meta 与 NSObject 不相等。
    所以 re3 的结果为 0。

    ret4 = 0 : 传入的cls 为 Person, self 指向 Person
    self->ISA( ) ,self的 isa 指向 Person meta ;Person meta 与 Person 不相等。
    所以 re4 的结果为 0。

    ret5 = 1 : 传入的cls 为 NSObject 类,self 指向 NSObject 的 实例对象
    第一次循环:tcls 指向 NSObject 类,cls 为 NSObject 类,执行判断 if (tcls == cls) ,相等,return YES,结束循环。
    所以 re5 返回 1。

    ret6 = 1 : 传入的cls 为 Person 类,self 指向 Person 的 实例对象
    第一次循环:tcls 指向 Person 类,cls 为 Person 类,执行判断 if (tcls == cls) ,相等,return YES,结束循环。
    所以 re6 返回 1。

    ret7 = 1 : 传入的cls 为 NSObject, self 指向 NSObject 对象
    [self class] 为 NSObject 类 ;与 cls 相等。
    所以 re7 的结果为 1。

    ret8 = 1 : 传入的cls 为 Person, self 指向 Person 对象
    [self class] 为 Person 类 ;与 cls 相等。
    所以 re8 的结果为 1。

    示例2

    #import <Foundation/Foundation.h>
    
    @interface LGPerson : NSObject
    - (void)sayHello;
    + (void)sayHappy;
    
    @end
    
    #import "LGPerson.h"
    
    @implementation LGPerson
    
    - (void)sayHello{
        NSLog(@"LGPerson say : Hello!!!");
    }
    
    + (void)sayHappy{
        NSLog(@"LGPerson say : Happy!!!");
    }
    
    @end
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            LGPerson *person = [LGPerson alloc];
            Class pClass     = object_getClass(person);
            lgObjc_copyMethodList(pClass);
    
            lgInstanceMethod_classToMetaclass(pClass);
            lgClassMethod_classToMetaclass(pClass);
            NSLog(@"Hello, World!");
        }
        return 0;
    }
    
    //获取类的方法列表
    void lgObjc_copyMethodList(Class pClass){
        unsigned int count = 0;
        Method *methods = class_copyMethodList(pClass, &count);
        for (unsigned int i=0; i < count; i++) {
            Method const method = methods[i];
            //获取方法名
            NSString *key = NSStringFromSelector(method_getName(method));
            
            LGLog(@"Method, name: %@", key);
        }
        free(methods);
    }
    
    //获取类的实例方法
    void lgInstanceMethod_classToMetaclass(Class pClass){
        
        const char *className = class_getName(pClass);
        Class metaClass = objc_getMetaClass(className);
        
        Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
        Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
    
        Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
        Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
        
        LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
    }
    
    //获取类的类方法
    void lgClassMethod_classToMetaclass(Class pClass){
        
        const char *className = class_getName(pClass);
        Class metaClass = objc_getMetaClass(className);
        
        Method method1 = class_getClassMethod(pClass, @selector(sayHello));
        Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
    
        Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
        // 元类 为什么有 sayHappy 类方法 0 1
        //
        Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
        
        LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
    }
    
    //获取方法的实现
    void lgIMP_classToMetaclass(Class pClass){
        
        const char *className = class_getName(pClass);
        Class metaClass = objc_getMetaClass(className);
    
        // - (void)sayHello;
        // + (void)sayHappy;
        IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
        IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));
    
        IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
        IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
    
        NSLog(@"%s-%p-%p-%p-%p",__func__,imp1,imp2,imp3,imp4);
    }
    

    打印结果:

    Method, name: sayHello
    lgInstanceMethod_classToMetaclass - 0x1000031b0-0x0-0x0-0x100003148
    lgClassMethod_classToMetaclass-0x0-0x0-0x100003148-0x100003148
    lgIMP_classToMetaclass-0x100001d10-0x7fff66861580-0x7fff66861580-0x100001d40
    2020-09-15 18:46:31.544820+0800 002-类方法归属分析[18280:408952] Hello, World!
    

    打印结果分析:
    1.lgObjc_copyMethodLists函数里面的class_copyMethodList函数

    /** 
     * 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 * _Nullable
    class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount) 
        OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    

    lgObjc_copyMethodList这个函数中,主要是获取LGPerson类中的方法列表,从实例方法存储在类中,类方法存储在元类中可以得知,LGPerson的方法列表打印结果为sayHello方法

    2.lgInstanceMethod_classToMetaclass 函数里面的class_getInstanceMethod 函数

    /** 
     * Returns a specified instance method for a given class.
     * 
     * @param cls The class you want to inspect.
     * @param name The selector of the method you want to retrieve.
     * 
     * @return The method that corresponds to the implementation of the selector specified by 
     *  e name for the class specified by e cls, or c NULL if the specified class or its 
     *  superclasses do not contain an instance method with the specified selector.
     *
     * @note This function searches superclasses for implementations, whereas c class_copyMethodList does not.
     */
    OBJC_EXPORT Method _Nullable
    class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
        OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
    

    主要是用于获取实例方法,通过开发文档苹果给出的说明是:如果在传入的类或者类的父类中没有找到指定的实例方法,则返回NULL

    3.lgClassMethod_classToMetaclass 函数里面的class_getClassMethod 函数

    /** 
     * Returns a pointer to the data structure describing a given class method for a given class.
     * 
     * @param cls A pointer to a class definition. Pass the class that contains the method you want to retrieve.
     * @param name A pointer of type c SEL. Pass the selector of the method you want to retrieve.
     * 
     * @return A pointer to the c Method data structure that corresponds to the implementation of the 
     *  selector specified by aSelector for the class specified by aClass, or NULL if the specified 
     *  class or its superclases do not contain an instance method with the specified selector.
     *
     * @note Note that this function searches superclasses for implementations, 
     *  whereas c class_copyMethodList does not.
     */
    OBJC_EXPORT Method _Nullable
    class_getClassMethod(Class _Nullable cls, SEL _Nonnull name)
        OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
    
    
    //获取类方法
    Method class_getClassMethod(Class cls, SEL sel)
    {
        if (!cls  ||  !sel) return nil;
    
        return class_getInstanceMethod(cls->getMeta(), sel);
    }
    
    //获取元类
     // NOT identical to this->ISA when this is a metaclass 判断是否是元类,是元类就直接返回,反之,继续找isa指向
    Class getMeta() {
        if (isMetaClass()) return (Class)this;
        else return this->ISA();
    }
    

    class_getClassMethod的实现是获取类的类方法,其本质就是获取元类的实例方法,最终还是会走到class_getInstanceMethod,但是在这里需要注意的一点是:在getMeta源码中,如果判断出cls是元类,那么就不会再继续往下递归查找,会直接返回this,其目的是为了防止元类的无限递归查找。

    4.lgIMP_classToMetaclass 函数里面的class_getMethodImplementation 函数

    /** 
     * Returns the function pointer that would be called if a 
     * particular message were sent to an instance of a class.
     * 
     * @param cls The class you want to inspect.
     * @param name A selector.
     * 
     * @return The function pointer that would be called if c [object name] were called
     *  with an instance of the class, or c NULL if e cls is c Nil.
     *
     * @note c class_getMethodImplementation may be faster than c method_getImplementation(class_getInstanceMethod(cls, name)).
     * @note The function pointer returned may be a function internal to the runtime instead of
     *  an actual method implementation. For example, if instances of the class do not respond to
     *  the selector, the function pointer returned will be part of the runtime's message forwarding machinery.
     */
    OBJC_EXPORT IMP _Nullable
    class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name) 
        OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    

    该函数在向类实例发送消息时会被调用,并返回一个指向方法实现函数的指针。这个函数会比method_getImplementation(class_getInstanceMethod(cls, name))更快。返回的函数指针可能是一个指向runtime内部的函数,而不一定是方法的实际实现。如果类实例无法响应selector,则返回的函数指针将是运行时消息转发机制的一部分
    下面我们也可以通过这个方法的源码来印证上面的这个说法:

    IMP class_getMethodImplementation(Class cls, SEL sel)
    {
        IMP imp;
    
        if (!cls  ||  !sel) return nil;
    
        //查找方法实现
        imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
    
        //如果没有找到,则进行消息转发
        if (!imp) {
            return _objc_msgForward;
        }
    
        return imp;
    }
    

    总结:

    class_getInstanceMethod:获取实例方法,如果指定的类或其父类不包含带有指定选择器的实例方法,则为NULL
    class_getClassMethod:获取类方法,如果指定的类或其父类不包含具有指定选择器的类方法,则为NULL。
    class_getMethodImplementation:获取方法的具体实现,如果未查找到,则进行消息转发

    未完待续···不断学习,不断更新




    每天都有不一样的太阳
  • 相关阅读:
    防删没什么意思啊,直接写废你~
    绝大多数情况下,没有解决不了的问题,只有因为平时缺少练习而惧怕问题的复杂度,畏惧的心理让我们选择避让,采取并不那么好的方案去解决问题
    Java 模拟面试题
    Crossthread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on
    一步步从数据库备份恢复SharePoint Portal Server 2003
    【转】理解 JavaScript 闭包
    Just For Fun
    The database schema is too old to perform this operation in this SharePoint cluster. Please upgrade the database and...
    Hello World!
    使用filter筛选刚体碰撞
  • 原文地址:https://www.cnblogs.com/YangFuShun/p/15154886.html
Copyright © 2011-2022 走看看