iOS底层番外01-iOS内省方法
iOS内省方法
作为一门面向对象的语言,OC具有内省(Introspection)这样一个强大的特性。用于揭示对象在运行时的详细信息,包括方法响应链、继承树上的位置、遵循的协议等。
下面是一些常用的内省方法。
+ (Class)superclass; // 获取类继承链上的父类
- (Class)superclass; // 获取实例的类继承链上的父类
+ (BOOL)isMemberOfClass:(Class)cls; // 判断类的元类是否是给定类
- (BOOL)isMemberOfClass:(Class)cls; // 判断实例的类是否是给定类
+ (BOOL)isKindOfClass:(Class)cls; // 判断类的元类是否在给定类继承链上
- (BOOL)isKindOfClass:(Class)cls; // 判断实例的类是否在给定类继承链上
+ (BOOL)respondsToSelector:(SEL)sel; // 类的元类中是否能查找到指定类方法
- (BOOL)respondsToSelector:(SEL)sel; // 实例的类中是否能查找到指定实例方法
+ (BOOL)instancesRespondToSelector:(SEL)sel; // 类中是否能查找到指定实例方法
+ (BOOL)conformsToProtocol:(Protocol *)protocol; // 类是否遵循指定协议并实现协议的@required方法
- (BOOL)conformsToProtocol:(Protocol *)protocol; // 实例的类是否遵循指定协议并实现协议的@required方法
一道内省的面试题
下面两个NSLog会输出什么?
BOOL objKind = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL objMember = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL customKind = [(id)[TheObjectOne alloc] isKindOfClass:[TheObjectOne class]];
BOOL customMember = [(id)[TheObjectOne alloc] isMemberOfClass:[TheObjectOne class]];
BOOL objClassKind = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL objClassMember = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL customClassKind = [(id)[TheObjectOne class] isKindOfClass:[TheObjectOne class]];
BOOL customClassMember = [(id)[TheObjectOne class] isMemberOfClass:[TheObjectOne class]];
NSLog(@"objKind:%hhd objMember:%hhd customKind:%hhd customMember:%hhd\n",objKind,objMember,customKind,customMember);
NSLog(@"objClassKind:%hhd objClassMember:%hhd customClassKind:%hhd customClassMember:%hhd\n",objClassKind,objClassMember,customClassKind,customClassMember);
先想一下再看答案:
objClassKind:1 objClassMember:0 customClassKind:0 customClassMember:0
objKind:1 objMember:1 customKind:1 customMember:1
很奇怪,类对象的内省方法差不多全军覆没,我不属于我自己是什么操作?带着问题我们来看一下isMemberOfClass和isKindOfClass源码,而它们又分别有自己的类方法和实例方法。
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
结合源码和上面类常用的内省方法后面的注释可以看出来实例方法isMemberOfClass和isKindOfClass是找到实例的类的继承链来和指定类作为比较,而类方法是找到自己的元类的继承链和指定类比较。
上面题目中实例调用isMemberOfClass和isKindOfClass方法时使用实例的类和指定类进行比较,而指定类就是实例所属的类,此时实例类毫无疑问在指定类继承链上,所以结果无疑全是真。
再来看类调用isMemberOfClass和isKindOfClass方法。类会在元类的继承链上找是否有自己的位置。理所当然类不会在元类继承链上出现,但是有个意外,NSObject返回为真。这个就不得不又掏出官方的isa走位图了。
可以看到图中根类NSObject的元类为根元类,而根元类的父类是谁?还是NSObject。没办法,谁让所有类都是NSObject的子类。NSObject绕了一圈处在了根元类的继承链上。所以[(id)[NSObject alloc] isKindOfClass:[NSObject class]]结果为真。
