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。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,825评论 6 546
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,814评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,980评论 0 384
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 64,064评论 1 319
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,779评论 6 414
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 56,109评论 1 330
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 44,099评论 3 450
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,287评论 0 291
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,799评论 1 338
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,515评论 3 361
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,750评论 1 375
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,221评论 5 365
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,933评论 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,327评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,667评论 1 296
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,492评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,703评论 2 380

推荐阅读更多精彩内容