目录
前言
认识这两个方法之前,首先要了解isa
指向流程和继承链(【iOS】类对象的结构分析)关系,以便理解得更透彻
上经典图:
要注意的是:根类(NSObject)的元类isa
是根元类,根元类的父类superclass
是NSObject
class方法
实例调用class
方法就是获取isa
所指:
objectivec
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
类调用class
方法就是返回本身Class
:
objectivec
typedef struct objc_class *Class;
+ (Class)class {
return self;
}
isMemberOfClass和isKindOfClass
查objc4
源码
objectivec
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
实例方法分析
- (BOOL)isMemberOfClass:(Class)cls
:
此方法用于判断self
(调用者)类型 与cls
(指定类 )是否是同一个类,是就返回YES
,否则返回NO
。因为每个类对象在内存中只存在一份,所以可以用==
判断
objectivec
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isKindOfClass:(Class)cls
:
此方法用于判断self
(调用者)类型 与cls
(指定类或者其父类 )是否是同一个类,是就返回YES
,否则就沿着继承链向上查找,只要有一个类与cls
使用一个类,就返回YES
,如果直到NSObject
还没有找到,再向上就为nil
,返回NO
objectivec
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
类方法分析
类方法的调用者是Class
类对象,按照isa
指向,对比的应该是meta-class
元类对象
+ (BOOL)isMemberOfClass:(Class)cls
:
此方法判断self
(调用者)的isa
(元类 )是否是指定元类(元类也是Class类型)
objectivec
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
+ (BOOL)isKindOfClass:(Class)cls
:
此方法实现与其对象方法的实现类似,只是对比的是元类及元类的继承链
判断self
(调用者)的isa
(元类 )是否是指定类的元类或者其父类的元类 ,是就返回YES
,否则就沿着继承链向上找到根元类
,最后如果沿着superclass
直到NSObject
还没找到,再向上就为nil
,返回NO
objectivec
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
实例验证
下面通过调用上面的方法,来验证以上的分析:
objectivec
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface Person : NSObject
@end
@implementation Person
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL res1 = [[NSObject class] isKindOfClass: [NSObject class]];
BOOL res2 = [[Person class] isKindOfClass: [Person class]];
BOOL res3 = [[NSString class] isKindOfClass: [NSObject class]];
NSLog(@"%d %d %d", res1, res2, res3); // 1 0 1
Person* person = [[Person alloc] init];
BOOL res4 = [[Person class] isKindOfClass: [NSObject class]];
BOOL res5 = [person isKindOfClass: [NSObject class]];
NSLog(@"%d %d", res4, res5); // 1 1
// 获取一个类的元类的两种方式
// Class objectMeta = objc_getMetaClass("NSObject");
Class objectMeta = object_getClass([NSObject class]);
// Class personMeta = objc_getClass("Person");
Class personMeta = object_getClass([Person class]);
// BOOL res6 = [[NSObject class] isMemberOfClass: objectMeta]; res6 == 1
BOOL res6 = [[NSObject class] isMemberOfClass: [NSObject class]];
BOOL res7 = [person isMemberOfClass: [NSObject class]];
// BOOL res8 = [[Person class] isMemberOfClass: personMeta]; res8 == 1
BOOL res8 = [[Person class] isMemberOfClass: [NSObject class]];
BOOL res9 = [person isMemberOfClass: [Person class]];
NSLog(@"%d %d %d %d" , res6, res7, res8, res9); // 0 0 0 1
}
return 0;
}
isMemberOfClass
res1
中调用的是类方法,沿着元类 继承链会 找到NSObject
类对象res2
中调用的是类方法,沿着元类 继承链不会 找到Person
类对象res3
中调用的是类方法,沿着元类 继承链会 找到NSObject
类对象res4
中调用的是类方法,沿着元类 继承链会 找到NSObject
类对象res5
中调用的是对象方法,沿着类 继承链会 找到NSObject
类对象
isKindOfClass
res6
中调用的是类方法,对比的是元类对象,而传入的是类对象res7
中调用的是对象方法,Person
显然跟NSObject
不是同一类res8
中调用的是类方法,对比的是元类对象,而传入的是类对象res9
中调用的是对象方法,person
是Person
类
上面的代码中,注释掉的res6
、res8
中传入的是相应的元类对象 ,则结果为1
总结
那张isa
指向和继承链流程图很经典!很重要!
结合源码会对这两个方法理解得更透彻
再分得清这两个方法的调用者是class
(类对象)还是instance
(实例对象)