美文网首页
通过源码认识iskindOfClass、isMemberOfCl

通过源码认识iskindOfClass、isMemberOfCl

作者: 我是菜Bob | 来源:发表于2020-09-16 11:55 被阅读0次

    苹果官方描述:

    1. Returns a Boolean value that indicates whether the receiver is an instance of given class or an instance of any class that inherits from that class.
    - (BOOL)isKindOfClass:(Class)aClass;
    2. Returns a Boolean value that indicates whether the receiver is an instance of a given class.
    - (BOOL)isMemberOfClass:(Class)aClass;

    isa、superClass走位图

    在进一步探索前我们要先通过下图了解OC中的:实例对象(instance)、类对象(class)、元类对象(meta)之间的关系

    image

    isa简单概括为:instance的isa-> class,class的isa-> meta,meta的isa->根元类,根元类的isa->指向根元类自己
    superClass简单概括为:1、class的superClass指向父类,Root class的superClass为nil;2、元类的superClass指向父元类,根元类的superClass指向Root class。

    Class相关源码

    结合这些Class相关的源码和isa走位图可以更加清晰的理解isKindOfClassisMemberOfClass的实现

    //参数`id obj`,中的obj包含实例对象(instance)、类对象(class)、元类对象(meta)
    //obj是实例对象(instance) 返回结果是类对象(class)
    //obj是类对象(class)返回的结果是元类对象(meta)
    //obj是元类对象(meta)返回的结果是根元类
    Class object_getClass(id obj)
    {
      if (obj) return obj->getIsa();
      else return Nil;
    }
    
    + (Class)class {
      return self;
    }
    
    - (Class)class {
      return object_getClass(self);
    }
    
    + (Class)superclass {
       return self->superclass;
    }
    
    - (Class)superclass {
      return [self class]->superclass;
    }
    

    isKindOfClass、isMemberOfClass源码实现

    isKindOfClass、isMemberOfClass两个方法的参数cls可以是类对象(class)、元类对象(meta)

    • isKindOfClass:
      类方法:self可以是类对象(class)、元类对象(meta)
      1、获取类对象的isa(元类对象)给tcls
      2、如果tcls存在,判断tcls与cls是否相等,相等返回YES,不相等继续对比tcls的superclass。
      3、tcls = tcls->superclass,一直找到根元类,如果根元类与cls也不相等,则tcls = Root class(meta)->superclass,既tcls = Root class。
      4、如果Root class与cls也不相等,tcls = Root class ->superclass,既tcls = nil,退出循环,返回NO。
      通过分析发现+ (BOOL)isKindOfClass:(Class)cls的参数传入如果是除了根类的其他类对象,则实际是用元类对象和类对象比较,一定会返回NO。

       + (BOOL)isKindOfClass:(Class)cls {
           for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
             if (tcls == cls) return YES;
           }
           return NO;
       }
      

      实例方法:self是类的实例对象,由上面的Class相关源码- (Class)class可知,[self class]的结果是实例对象的isa指向的类对象,既 self对应的类,tcls = 类对象(class)。
      如果tcls与cls不相等,则判断tcls->superclass与cls是否相等,直到tcls = Root Class-> superclass,既tcls = nil退出循环。
      通过分析发现这个循环中并没有获取到对象的元类进行比较,所以实例对象调用isKindOfClass:方法时如果参数传入的是元类,则一定返回NO。

       - (BOOL)isKindOfClass:(Class)cls {
          for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
            if (tcls == cls) return YES;
           }
         return NO;
       }
      

    1、通过对上面isKindOfClass类方法、实例方法的分析发现,由于Root class-> superclass是nil,根元类的superclass是Root class,所以不会进行死循环。
    2、判断一个对象和其指定的类之间的关系:实例对象-类对象,类对象-元类,(由于跟元类的superclass指向根类,所以这是个特殊情况,可以进类对象-类对象、元类-元类)
    3、验证了官方描述:isKindOfClass用来判断一个对象是否是指定类或者该类的子类的实例对象。从这里也反应了类是其元类的对象,所以称为类对象

    isKindOfClass:的实际调用

    当进入断点调试时发现isKindOfClass:并不会进入上面分析的源码,在isKindOfClass:断点,按住control,点击(Xcode调试step into按钮:step into instruction (hold control)),会进入汇编调用:

      object-study`objc_opt_isKindOfClass:
        ->  0x100001884 <+0>: jmpq   *0x17a6(%rip) ; (void *)0x00000001000018e4
    

    实际调用了objc_opt_isKindOfClass,这是因为llvm编译器对isKindOfClass方法进行了优化。下面来看下objc_opt_isKindOfClass的实现:

    // Calls [obj isKindOfClass]
    BOOL objc_opt_isKindOfClass(id obj, Class otherClass)
    {
        #if __OBJC2__
        if (slowpath(!obj)) return NO;
        Class cls = obj->getIsa();
        if (fastpath(!cls->hasCustomCore())) {
            for (Class tcls = cls; tcls; tcls = tcls->superclass) {
                if (tcls == otherClass) return YES;
            }
            return NO;
         }
         #endif
        return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
    }
    

    重点内容:

    //获取isa,实例对象的isa是类对象,类对象的isa是元类。
    //这里优化了+isKindOfClass:和-isKindOfClass:
    //等价于+isKindOfClass:方法中的Class tcls = self->ISA()
    //等价于-isKindOfClass:方法中的Class tcls = [self class]
    Class cls = obj->getIsa();
    if (fastpath(!cls->hasCustomCore())) {
        //循环继承链,对比otherClass是否在继承链上
        for (Class tcls = cls; tcls; tcls = tcls->superclass) {
            if (tcls == otherClass) return YES;
        }
        return NO;
     }
    

    分析发现结果和我们上面对+isKindOfClass:-isKindOfClass:分析的一致

    • isMemberOfClass:
      类方法:self可以是类对象(class)、元类对象(meta)
      1、self是类对象,则self->ISA()是其元类。
      2、self是元类对象,则self->ISA()是根元类。
      那么self->ISA() == cls就是判断一个类对象或元类对象是否是指定类的实例对象。

      + (BOOL)isMemberOfClass:(Class)cls {
          return self->ISA() == cls;
      }
      

      实例方法:self是类的实例对象,由上面的Class相关源码- (Class)class可知,[self class]的结果是实例对象的isa指向的类对象,既self对应的类。那么[self class] == cls就是判断实例对象是否是类对象的实例对象。

      - (BOOL)isMemberOfClass:(Class)cls {
          return [self class] == cls;
      }
      

    验证了官方描述:isMemberOfClass用来判断一个对象是否是指定类的实例对象。从这里也反应了类是其元类的对象,元类是根元类的对象,和isa走位图吻合

    isKindOfClassisMemberOfClass相同点都是判断对象和其指定类的关系,不同点是isKindOfClass包含了继承关系,可以判断是否是其类的子类对象,isMemberOfClass不包含继承关系。

    代码验证

    #import <Foundation/Foundation.h>
    #import <objc/runtime.h>
    @interface STPerson : NSObject
    @end
    @implementation STPerson
    @end
    int main(int argc, const char * argv[]) {
    
    @autoreleasepool {
    
        NSObject *rootObj = [NSObject alloc];//实例对象
    
        //[rootObj class]和[NSObject class]是相同的
    
        //Class rootClass = [NSObject class];
    
        Class rootClass = [rootObj class];//Root class 类对象
    
        Class rootMeta = object_getClass(rootClass); //根元类
        NSLog(@"【[rootObj class]=[NSObject class]】%d",[rootObj class]==[NSObject class]); //1
    
        NSLog(@"【rootMeta is Meta】 %d",class_isMetaClass(rootMeta));  //1
    
        STPerson *person = [STPerson alloc];//STPerson 实例对象
    
        Class personClass = [person class];//STPerson 类对象
    
        Class personMeta = object_getClass(personClass); //STPerson 元类
    
        NSLog(@"【personMeta is Meta】%d",class_isMetaClass(personMeta)); //1
    
        NSLog(@"***** + (BOOL)isKindOfClass:(Class)cls ******");
    
        NSLog(@"【Root class-Root class】    %d",[rootClass isKindOfClass:rootClass]);
    
        NSLog(@"【Root Meta-Root Meta】    %d",[rootMeta isKindOfClass:rootMeta]); 
    
        NSLog(@"【Root class-Root Meta】    %d",[rootClass isKindOfClass:rootMeta]);
    
        NSLog(@"【personClass-Root class】  %d",[personClass isKindOfClass:rootClass]);   
    
        NSLog(@"【personClass-personClass】  %d",[personClass isKindOfClass:personClass]);
    
        NSLog(@"【personMeta-personMeta】    %d",[personMeta isKindOfClass:personMeta]);   
    
        NSLog(@"【personClass-personMeta】  %d",[personClass isKindOfClass:personMeta]); 
    
        NSLog(@"【personMeta-personClass】  %d",[personClass isKindOfClass:personClass]); 
    
        //+ (BOOL)isKindOfClass:(Class)cls的打印结果是:1、1、1、1、0、0、1、0
    
        NSLog(@"***** -(BOOL)isKindOfClass:(Class)cls *****");
    
        NSLog(@"【rootObj-rootMeta】      %d",[rootObj isKindOfClass:rootMeta]);   
    
        NSLog(@"【rootObj-rootClass】    %d",[rootObj isKindOfClass:rootClass]);   
    
        NSLog(@"【person-rootMeta】      %d",[person isKindOfClass:rootMeta]);     
    
        NSLog(@"【person-personMeta】    %d",[person isKindOfClass:personMeta]);   
    
        NSLog(@"【person-personClass】    %d",[person isKindOfClass:personClass]); 
    
        NSLog(@"【person-rootClass】      %d",[person isKindOfClass:rootClass]); 
    
        //- (BOOL)isKindOfClass:(Class)cls的打印结果是:0、1、0、0、1、1
    
        NSLog(@"***** + (BOOL)isMemberOfClass:(Class)cls *****");
    
        NSLog(@"【Root Meta-Root Meta】      %d",[rootMeta isMemberOfClass:rootMeta]); 
    
        NSLog(@"【personMeta-Root Meta】    %d",[personMeta isMemberOfClass:rootMeta]);
    
        NSLog(@"【rootClass-rootClass】      %d",[rootClass isMemberOfClass:rootClass]);
    
        NSLog(@"【rootClass-rootMeta】      %d",[rootClass isMemberOfClass:rootMeta]);
    
        NSLog(@"【personClass-Root Meta】    %d",[personClass isMemberOfClass:rootMeta]);
    
        NSLog(@"【personClass-personMeta】  %d",[personClass isMemberOfClass:personMeta]);
    
        //+ (BOOL)isMemberOfClass:(Class)cls的打印结果是:1、1、0、1、0、1
    
        NSLog(@"***** - (BOOL)isMemberOfClass:(Class)cls *****");
    
        NSLog(@"【rootObj-rootMeta】  %d",[rootObj isMemberOfClass:rootMeta]);
    
        NSLog(@"【rootObj-rootClass】  %d",[rootObj isMemberOfClass:rootClass]);
    
        NSLog(@"【person-rootMeta】    %d",[person isMemberOfClass:rootMeta]);
    
        NSLog(@"【person-rootClass】  %d",[person isMemberOfClass:rootClass]);
    
        NSLog(@"【person-personClass】 %d",[person isMemberOfClass:personClass]);
    
        NSLog(@"【person-personMeta】  %d",[person isMemberOfClass:personMeta]);
    
      //- (BOOL)isMemberOfClass:(Class)cls的打印结果是:0、1、0、0、1、0
    
      }
      return 0;
    }
    

    如果有歧义、错误的地方,欢迎留言指出,以免误导大家。

    相关文章

      网友评论

          本文标题:通过源码认识iskindOfClass、isMemberOfCl

          本文链接:https://www.haomeiwen.com/subject/thpcyktx.html