通过源码认识iskindOfClass、isMemberOfClass

苹果官方描述:

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 
#import 
@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、isMemberOfClass)