iOS 内省的几个方法

首先要用到这么一张图


什么是内省?

在计算机科学中,内省是指计算机程序在运行时(Runtime)检查对象(Object)类型的一种能力,通常也可以称作运行时类型检查。 不应该将内省和反射混淆。
相对于内省,反射更进一步,是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。

内省的几个方法

isMemberOfClass //对象是否是某个类型的 对象

+isMemberOfClass 类方法是直接判断当前类的isa指向 (也就是当前类的元类) 和对比类是否相等。
-isMemberOfClass 对象方法是直接判断 [self class] (当前类) 和对比类是否相等。

isKindOfClass //对象是否是某个类型或某个类型子类的 对象

+isKindOfClass 类方法是从当前类的isa指向 (也就是当前类的元类) 开始,沿着 superclass 继承链查找判断和对比类是否相等。
-isKindOfClass 对象方法是从 [self class] (当前类) 开始,沿着 superclass 继承链查找判断和对比类是否相等。

isSubclassOfClass//某个类对象 是否是另一个类型的子类
isAncestorOfObject //某个类对象 是否是另一个类型的父类
respondsToSelector //是否能响应某个方法
conformsToProtocol //是否遵循某个协议

isKindOfClass 与 isMemberOfClass比较代码

@interface Sark : NSObject
@end
 
@implementation Sark
@end
 
int main(int argc, const char * argv[]) {
  @autoreleasepool {
      BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
      BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
      BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]];
      BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]];
      NSLog(@"%d %d %d %d", res1, res2, res3, res4);

      BOOL res5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
      BOOL res6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
      BOOL res7 = [(id)[Sark alloc] isKindOfClass:[Sark class]];
      BOOL res8 = [(id)[Sark alloc] isMemberOfClass:[Sark class]];
      NSLog(@"%d %d %d %d", res5, res6, res7, res8);
  }
  return 0;
}

//1 0 0 0
//1 1 1 1

-我们先看看isKindOfClass

  • +isKindOfClass

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

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

调试最终的代码并不会运行到此处,这是因为在编译时系统经过编译优化,所以最终会运行到objc_opt_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);
}

从源码可以看到:

  • 如果调用者obj是类类型,我们知道类的isa指向该类的元类,所以这时会走元类meta class的继承链条进行查找判断,先判断class是否等于meta class,不等就继续循环判断是否等于super class,不等再继续取,如此循环下去。
  • 如果调用者obj是对象,那么最终会获取该对象的isa然后走该对象的类的继承链进行查找判断

+isKindOfClass

  • [NSObject class]执行完之后调用isKindOfClass

第一次判断先判断[NSObject class][NSObject class] 的meta class是否相等,从图上我们也可以看出,[NSObject class]的meta class[NSObject class]不等;

第二次循环判断[NSObject class]meta class的superclass是否相等。还是从那张图上面我们可以看到:Root class(meta) 的superclass 就是 Root class(class),也就是[NSObject class]。所以第二次循环相等,于是第一行res1输出应该为YES。如下图:

[NSObject class]

  • [Sark class]执行完之后调用isKindOfClass;

第一次for循环,Sark的Meta Class[Sark class]不等;

第二次for循环Sark Meta Class的super class 指向的是 Root class(meta), 和 [Sark class]不相等;

第三次for循环,Root class(meta)super class指向的是Root class(class),也就是NSObject本身,和 [Sark class] 不相等。

第四次循环,Root class(class)super class 指向 nil, 和 [Sark class]不相等。四次循环之后,退出循环,所以第三行的res3输出为NO。

[Sark class]

-isKindOfClass

如果把这里的Sark改成它的实例对象,[sark isKindOfClass:[Sark class],那么此时就应该输出YES了。

此处描述错误
因为在isKindOfClass函数中,判断sark的meta class是自己的元类Sark,第一次for循环就能输出YES了。

-isKindOfClass源码我们可以看到,对象方法的 for循环 初始值 变成了 [self class],也就是从当前类开始找superclass继承链。
所以 [(id)[NSObject alloc] isKindOfClass:[NSObject class]] 和 [(id)[sark alloc] isKindOfClass:[sark class]] 都为 YES。

-接着看看isMemberOfClass

isMemberOfClass源码

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

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

+isMemberOfClass的源码实现是拿到自己的isa指针(也就是当前类的元类) 和自己比较,是否相等。
res2[NSObject class]的isa 指向 [NSObject class] 的 Meta Class,所以和 [NSObject class]不相等;
res4,[Sark class]的isa指向[Sark class]的Meta Class,和[Sark class]也不等;
所以res2res4都输出NO。
-isMemberOfClass对象方法更是简单了,直接就是判断当前类和传入类是否相等。所以res6res8都输出YES。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容