Objective-C内省/自省

内省和自省是一个概念,下文使用内省替代;

什么是内省(Introspection)?

在计算机科学中,内省是指计算机程序在运行时(Runtime)检查对象(Object)类型的一种能力,通常也可以称作“运行时类型检查”。一些编程语言如C++、Java、Ruby、PHP、Objective-C、Perl等等具有这种特性。

这个维基百科中对于内省的解释

Objective-C 内省

官方说明

自翻勿喷

Objective-C 中的内省方法都定义在NSObject协议中。

Objective-C内省.png

除此之外还有些人认为 + instancesRespondToSelector:或者 + conformsToProtocol:也是内省方法,个人认为这两个不能算是内省方法,虽然它们可以做到 respondsToSelectorconformsToProtocol一样的效果,但由于它们是类方法,也就是说它们的方法调用者并不是一个实例对象,所以不能将其归到内省方法中。

isKindOfClass

直接来看runtime源码吧,isKindOfClass最终调用就是 NSObject.mm 中的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);
}
复制代码

在当前环境下,执行的都是 Objective-C 2.0 的代码。关于 runtime 中的宏 请看这里

先看这一句 if (slowpath(!obj)) return NO;,表示如果 obj 是个 nil 的话,直接返回 NO
Class cls = obj->getIsa(); 通过实例对象的 isa 指针获取它的 Class 对象 clsfastpath(!cls->hasCustomCore()) 表示是否自己实现了 new/self/class/respondsToSelector/isKindOfClass 的实例方法,没有实现的话就会执行 if 内部的代码。

这个 for 循环比较简单,就是从 obj对应的 Class 对象沿着继承关系开始寻找和 otherClass 相同的 Class 对象。顺手提一句 == 大家都知道是内存地址的比较,tcls == otherClass相等,说明他们的内存是相同的,也从侧面说明 Class 对象也是对象,存在于内存中。

isMemberOfClass

稍微修改一下代码

CZPerson *objc = [[CZPerson alloc] init];
[objc isMemberOfClass:[NSObject class]];
复制代码

在来看一下调用过程

isMemberOfClass.png

这和 isKindOfClass 有点区别,使用的是消息发送,说明它底层是调用了 Objective-C 方法。 在NSObject.mm 中有 isMemberOfClass 的默认实现

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

从这里看出 isMemberOfClass 逻辑比较简单,就是比较两个 Class 对象的内存地址是否相同,也就是调用者的Class和参数Class必须是同一个。

respondsToSelector

respondsToSelector 指的是对象是否 实现 了某个方法,如果只是声明没有实现的话,返回值为 NO。看下源码更加直观。

CZPerson *objc = [[CZPerson alloc] init];
[objc respondsToSelector:@selector(testMethod)];
复制代码

再来看一下调用过程

respondsToSelector.png

我们来看一下 NSObject.mm 中的 objc_opt_respondsToSelector实现

// Calls [obj respondsToSelector]
BOOL
objc_opt_respondsToSelector(id obj, SEL sel)
{
#if __OBJC2__
    if (slowpath(!obj)) return NO;
    Class cls = obj->getIsa();
    if (fastpath(!cls->hasCustomCore())) {
        return class_respondsToSelector_inst(obj, sel, cls);
    }
#endif
    return ((BOOL(*)(id, SEL, SEL))objc_msgSend)(obj, @selector(respondsToSelector:), sel);
}
复制代码

这里的逻辑和 isKindOfClass 有点类似,就不做赘述了,直接说结果。如果对象没有重写 respondsToSelector ,那么就调用 class_respondsToSelector_inst 返回结果。

// inst is an instance of cls or a subclass thereof, or nil if none is known.
// Non-nil inst is faster in some cases. See lookUpImpOrForward() for details.
NEVER_INLINE __attribute__((flatten)) BOOL
class_respondsToSelector_inst(id inst, SEL sel, Class cls)
{
    // Avoids +initialize because it historically did so.
    // We're not returning a callable IMP anyway.
    return sel && cls && lookUpImpOrNilTryCache(inst, sel, cls, LOOKUP_RESOLVER);
}


IMP lookUpImpOrNilTryCache(id inst, SEL sel, Class cls, int behavior)
{
    return _lookUpImpTryCache(inst, sel, cls, behavior | LOOKUP_NIL);
}
复制代码

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享