Objective-C中的自省函数(isKindOfClass & isMemberOfClass)

1,209 阅读3分钟

前言

OC中的自省函数有两个 isKindOfClass & isMemberOfClass,开发中应该经常会用到.

函数作用是用来判断对象是否是某个类或者其子类对象,平时使用时多为实例对象判断是否为类对象的实例,今天在准备面试题目时,发现对这两个函数理解的还不是很深入,所以查询源码后分享一下实现原理,面试题也会放在测试环节说明.

相关知识

如果想理解自省函数,需要对oc对象的底层逻辑有所了解,主要是对象、类对象、元类对象之间的关系以及superClass指针和isa指针,这里不做扩展,自行了解

相关函数object_getClass() & class()

  • object_getClass() : 函数的作用是获取入参对象的isa指针指向的对象,例如如果入参为实例对象,它的isa指针指向类对象,如果入参是类对象,则isa指向mate Class元类对象.如果入参为元类对象,则isa指向root mate Class
Class object_getClass(id obj) {
    if(obj) return obj->getIsa();
    else return Nil;
}
  • class() : 函数用来获取类对象,分为实例方法和类方法.实例函数返回他的类对象,类函数返回类对象本身
- Class class() {
    return object_getClass(self);
}

+ Class class() {
    return self;
}

isKindOfClass

函数作用: 判断对象是否为入参类型的对象,实现源码如下:

+ (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;
}

函数分为类函数和实例函数,二者的区别在于循环条件初始值的不同,

对于类函数来说,Class tcls = object_getCLass(self) 初始值为类对象self的isa指针指向的对象(即为mateClass元类对象),简单说就是是从元类对象开始对比,然后沿着superClass指针查找,直接指向nil或者找到,结束遍历

对于实例函数来说,Class tcls = [self class] 是从实例对象的类对象开始比较的,而上面也提到,[self class] 其实等同于调用object_getClass(self) 返回类对象

总结两个方法,初始值都是从调用对象的isa指针指向的对象开始判断,

isMemberOfClass

函数作用类似于isKindOfClass,区别在于,只会判断入参和调用对象的isa指针是否相同,而不会遍历查找,代码如下:

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

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

根据class()的实现,两个函数实现是一样的,都是return object_getClass(self) == cls;

相关面试题

了解了两个函数的实现之后,再来看面试题就会轻松很多了,可以先自己判断返回值,然后查看打印结果对比验证

   // CFPerson : NSObject
   
   // NSObject.class()调用 + class()返回类对象本身,
   // isKindOfClass 是从对象isa指向的对象开始遍历,即mateClass,二者不同,掉用superClass然后继续对比,mateClass的superClass指向root mateClass时,再调用superClass则指向了root Class(OC中为NSObject);NSObject == NSObject,所以返回1
    NSLog(@"%hhd", [[NSObject class] isKindOfClass:[NSObject class]]);

    // 实例对象的class指向类对象,还是NSObject类对象 和NSObject.class等价
    NSLog(@"%hhd", [[obj class] isKindOfClass:[NSObject class]]);

    // 调用实例方法kindofclass, obj.class->NSObject 对比NSObject,返回1
    NSLog(@"%hhd", [obj isKindOfClass:[NSObject class]]);

    // CFPerson对比元类对象,返回false,通过superclass继续查找知道NSObject->nil, 最终返回0
    NSLog(@"%hhd", [[CFPerson class] isKindOfClass:[CFPerson class]]);

    //类方法, CFPerson.mateclass 和CFPerson类对象比较,返回0, CFPerson.mateClass.superclass->NSObject->nil 最终返回0
    NSLog(@"%hhd", [[student class] isKindOfClass:[CFPerson class]]);

    // 实例函数, CFPerson = cls tls = CFPerson
    NSLog(@"%hhd", [student isKindOfClass:[CFPerson class]]);
    
    // 元类不等于类对象,所以返回0
    NSLog(@"%hhd", [[NSObject class] isMemberOfClass:[NSObject class]]);
    
    // 同上,元类不等于类对象,返回0
    NSLog(@"%hhd", [[obj class] isMemberOfClass:[NSObject class]]);
    
    // 类对象等于,返回1
    NSLog(@"%hhd", [obj isMemberOfClass:[NSObject class]]);
    
    // 元类对象不等于类对象,返回0
    NSLog(@"%hhd", [[CFPerson class] isMemberOfClass:[CFPerson class]]);
    
    // 同上返回0
    NSLog(@"%hhd", [[student class] isMemberOfClass:[CFPerson class]]);
    
    // 类对象等于,返回1
    NSLog(@"%hhd", [student isMemberOfClass:[CFPerson class]]);

最后

由于刚开始学习写博客,文笔水平有限,如果你有不同的理解或想法,欢迎交流