目录
- 前言
- class方法
- isMemberOfClass和isKindOfClass
- 实例方法分析
- 类方法分析
- 实例验证
- 总结
前言
认识这两个方法之前,首先要了解isa
指向流程和继承链(【iOS】类对象的结构分析)关系,以便理解得更透彻
上经典图:
要注意的是:根类(NSObject)的元类isa
是根元类,根元类的父类superclass
是NSObject
class方法
实例调用class
方法就是获取isa
所指:
- (Class)class {return object_getClass(self);
}Class object_getClass(id obj)
{if (obj) return obj->getIsa();else return Nil;
}
类调用class
方法就是返回本身Class
:
typedef struct objc_class *Class;+ (Class)class {return self;
}
isMemberOfClass和isKindOfClass
查objc4
源码
+ (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
。因为每个类对象在内存中只存在一份,所以可以用==
判断
+ (BOOL)isMemberOfClass:(Class)cls {return self->ISA() == cls;
}
- (BOOL)isKindOfClass:(Class)cls
:
此方法用于判断self
(调用者)类型与cls
(指定类或者其父类)是否是同一个类,是就返回YES
,否则就沿着继承链向上查找,只要有一个类与cls
使用一个类,就返回YES
,如果直到NSObject
还没有找到,再向上就为nil
,返回NO
- (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类型)
+ (BOOL)isMemberOfClass:(Class)cls {return self->ISA() == cls;
}
+ (BOOL)isKindOfClass:(Class)cls
:
此方法实现与其对象方法的实现类似,只是对比的是元类及元类的继承链
判断self
(调用者)的isa
(元类)是否是指定类的元类或者其父类的元类,是就返回YES
,否则就沿着继承链向上找到根元类
,最后如果沿着superclass
直到NSObject
还没找到,再向上就为nil
,返回NO
+ (BOOL)isKindOfClass:(Class)cls {for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {if (tcls == cls) return YES;}return NO;
}
实例验证
下面通过调用上面的方法,来验证以上的分析:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>@interface Person : NSObject@end@implementation Person@endint 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 1Person* 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 == 1BOOL res6 = [[NSObject class] isMemberOfClass: [NSObject class]];BOOL res7 = [person isMemberOfClass: [NSObject class]];
// BOOL res8 = [[Person class] isMemberOfClass: personMeta]; res8 == 1BOOL 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
(实例对象)