isKindOfClass 和 isMemberOfClass 区别深究

12 篇文章 0 订阅

首先来看一道经典面试题:

以下代码的打印结果是什么?

 BOOL rs1 = [[NSObject class] isKindOfClass:[NSObject class]];
 BOOL rs2 = [[NSObject class] isMemberOfClass:[NSObject class]];
 BOOL rs3 = [[Person class] isKindOfClass:[Person class]];
 BOOL rs4 = [[Person class] isMemberOfClass:[Person class]];

 NSLog(@"rs1 = %d,rs2 = %d,rs3 = %d,rs4 = %d",rs1,rs2,rs3,rs4);

正确的结果应该是:rs1 = 1,rs2 = 0,rs3 = 0,rs4 = 0

为什么结果会是这个样子呢?这个地方我们不得不先插入几个概念:

isa、元类

OC的底层80%是C语言,平常创建的对象(类也属于对象,类对象)都转换成了底层C语言里面的结构体。

打开objc.h文件可以看到如下代码:

/// A pointer to an instance of a class.
typedef struct objc_object *id;
#endif

从定义来看,id是一个指向结构体objc_object的指针,同样在本文件中可以查看到结构体objc_object的定义:

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

结构体objc_object内部定一个指针isa,这个isa指向了Class,Class又是什么?同样在objc.h里面找到了这个Class的定义:

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

这个Class是一个指向结构体objc_class的一个指针,而这个objc_class的定义在runtime.h里面做了详细的介绍:

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

objc_class里面定义了关于这个类的一些信息,比如varsion(版本)、objc_ivar_list(变量列表)、objc_method_list(方法列表)等。值得注意的的是objc_class内部同objc_objcec一样也包含了一个isa,而这个isa是则是指向类对象的元类(meta)的。

关系如图:

注:虚线即表示isa指针指向。

第一列表示的是初始化的对象,也就是刚刚提到过的objc_object,这个对象里面有一个isa指针指向它所属的类,也就是图中的第二列;objc_class结构体里面存放的isa指针即指向了它的元类,也就是第三列,第三列的所有元类都指向了上帝类NSObject的元类(meta)。从图上可以看出,上帝类NSObject的元类最后又指向了NSObject这个类本身,所以构成了一个回路。

简单的了解了对象、类、元类的关联以后,来看一下一开始提到的那个面试题。

[NSObject class] 这是一个类的调用方法,calss内部是这样实现的:

+ (Class)class {
    return self;
}

 很明显,返回的是当前的调用者本身,也就是NSObject这个类。

再来看一下isKindOfClass和isMemberOf的内部实现:

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

从上面的代码中可以看的出来,一个循环语句,cls类指向的是isa,简单的说是在循环找父类。

BOOL rs1 = [[NSObject class] isKindOfClass:[NSObject class]];

第一次比较:拿着方法左边 [NSObject class](即NSObject本身)tcls,调用了object_getClass方法

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

object_getClass方法返回的是对象的isa指针,NSObject类的isa指针指向NSObject的元类,方法右边[NSObject class](即NSObject本身)cls做比较。而左边[NSObject class](即NSObject本身)的isa指向的是当前类对应的元类(meta class)。很明显,左边的meta class和右边的NSObject不想等,所以返回的是NO。

第二次比较:由上图可知,NSObject的元类(meta class)的父类是NSObject,所以这一次循环取meta class 的父类(即NSObjce),左边NSObjec = 右边NSObjec,所以 rs1 = 1.

同理剩余的三个可以验证。
 

isKindOfClassismemberofclass的区别:

isKindOfClass是进行循环地查找调用者及其父类是否和后者属于同一类;

ismemberofclass仅仅比较调用者的isa指向类是否和后者属于同一类。

如果题目中的

 BOOL rs3 = [[Person class] isKindOfClass:[Person class]];

变成

 BOOL rs3 = [[Person New] isKindOfClass:[Person class]];

返回的结果就是了。

顺便补充一句:

object_getClass:获得的是isa的指向
self.class:当self是实例对象的时候,返回的是类对象,否则返回自身

值得注意的是:类方法和实例方法有很大的不同,这个和类对象和实例对象的本质有关,所以查找的方式也会有不同。

+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

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

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

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

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值