1、面试题

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

  1. NSLog(@"%d",[Person isMemberOfClass:[Person class]]);
  2. NSLog(@"%d",[Person isKindOfClass:[Person class]]);
  3. NSLog(@"%d",[NSObject isMemberOfClass:[NSObject class]]);
  4. NSLog(@"%d",[NSObject isKindOfClass:[NSObject class]]);

想要知道打印结果,需要先知道isMemberOfClass:和isKindOfClass:的底层原理。

2、isMemberOfClass:和isKindOfClass:对象方法

查看 objc源码(NSObject.mm):

  1. - (BOOL)isMemberOfClass:(Class)cls {
  2. return [self class] == cls;
  3. }
  4. - (BOOL)isKindOfClass:(Class)cls {
  5. for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
  6. if (tcls == cls) return YES;
  7. }
  8. return NO;
  9. }

由源码可知:
-isMemberOfClass:是用于判断receiver是否是传入的cls类。
-isKindOfClass:是用于判断receiver是否是传入的cls类或cls类的子类。
例如创建一个Person类继承于NSObject,进行如下判断:

  1. Person *person = [[Person alloc] init];
  2. NSLog(@"%d",[person isMemberOfClass:[Person class]]);
  3. NSLog(@"%d",[person isMemberOfClass:[NSObject class]]);
  4. NSLog(@"%d",[person isKindOfClass:[NSObject class]]);
  5. NSLog(@"%d",[person isKindOfClass:[Person class]]);

打印结果:

  1. ~: 1
  2. ~: 0
  3. ~: 1
  4. ~: 1

3、isMemberOfClass:和isKindOfClass:类方法

查看 objc源码

  1. + (BOOL)isMemberOfClass:(Class)cls {
  2. return self->ISA() == cls;
  3. }
  4. + (BOOL)isKindOfClass:(Class)cls {
  5. for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
  6. if (tcls == cls) return YES;
  7. }
  8. return NO;
  9. }

由源码可知:
+isMemberOfClass:是用于判断receiver的元类是否是传入的cls元类
+isKindOfClass:是用于判断receiver是否是传入的cls元类类或cls元类的子类。
例如创建一个Person类继承于NSObject,进行如下判断:

  1. NSLog(@"%d",[Person isMemberOfClass:[Person class]]);
  2. NSLog(@"%d",[Person isKindOfClass:[Person class]]);
  3. NSLog(@"%d",[Person isMemberOfClass:object_getClass([Person class])]);
  4. NSLog(@"%d",[Person isKindOfClass:object_getClass([Person class])]);
  5. NSLog(@"%d",[Person isKindOfClass:[NSObject class]]);

打印结果:

  1. ~: 0
  2. ~: 0
  3. ~: 1
  4. ~: 1
  5. ~: 1

上面两个判断结果是0,因为类方法判断传入的应该是元类,[Person class]是类对象,所以结果是0。
但是有个比较特殊的情况,NSObject元类的superclass指针指向的是NSObject的类对象(可参考:isa、superclass),所以类方法isKindOfClass传入[NSObject class]结果是YES。

4、总结

在了解isMemberOfClass和isKindOfClass的原理后,就可以确定以下代码打印结果

  1. NSLog(@"%d",[Person isMemberOfClass:[Person class]]);
  2. NSLog(@"%d",[Person isKindOfClass:[Person class]]);
  3. NSLog(@"%d",[NSObject isMemberOfClass:[NSObject class]]);
  4. NSLog(@"%d",[NSObject isKindOfClass:[NSObject class]]);

打印结果:

  1. ~: 0
  2. ~: 0
  3. ~: 0
  4. ~: 1

分析:
第一行和第二行:传入的参数[Person class],是类对象,receiver是Person的类对象,遍历receiver的元类和元类的父类不会找到[Person class]类对象,所以返回结果是NO。
第三行和第一行、第二行原理相同。
第四行:传入参数是[NSObject class],是类对象,receiver是NSObject的类对象,遍历receiver的元类和元类的父类,会找到[NSObject class]对象,所以返回结果是YES。