iskindOfClass 与 isMemberOfClass 的底层原理与区别
一、示例展示
- 分析以下代码:
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[YDWPerson class] isKindOfClass:[YDWPerson class]];
BOOL re4 = [(id)[YDWPerson class] isMemberOfClass:[YDWPerson class]];
NSLog(@"\nre1 : %hhd\n re2 : %hhd\n re3 : %hhd\n re4 : %hhd\n", re1, re2, re3, re4);
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[YDWPerson alloc] isKindOfClass:[YDWPerson class]];
BOOL re8 = [(id)[YDWPerson alloc] isMemberOfClass:[YDWPerson class]];
NSLog(@"\nre5 : %hhd\n re6 : %hhd\n re7 : %hhd\n re8 : %hhd\n", re5, re6, re7, re8);
- 打印结果如下:
re1 : 1
re2 : 0
re3 : 0
re4 : 0
re5 : 1
re6 : 1
re7 : 1
re8 : 1
二、源码分析
- isKindOfClass 源码解析(实例方法 & 类方法)
- // - isKindOfClass:第一次对比 对象类与传入类 ,如果不相等,再对比是继续获取 上次类的父类与传入类 进行对比;
- // + isKindOfClass:第一次对比获取 类的元类与传入类 ,再次之后的对比是获取 上次结果的父类与传入类 进行对比;
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
+ (BOOL)isSubclassOfClass:(Class)cls {
for (Class tcls = self; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- isMemberOfClass 源码解析(实例方法 & 类方法)
- // + isMemberOfClass : 获取 类的元类 ,对比 传入类 ;
- // - isMemberOfClass : 获取 对象的类 ,对比 传入类 ;
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
三、示例分析
① 类方法
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
- re1 : 1 ,使用 +isKindOfClass ,对比 NSObject 与 NSObject ;
- 传入的 cls 为 NSobject , self 指向 NSobject ,进入循环;
- tcls 为 NSobject meta , cls 为 NSobject ;执行判断条件 if (tcls == cls) ,不相等执行 tcls = tcls->superclass ,此时 tcls 指向 NSobject meta 的父类 ,即 NSObject 。
- 再次循环,此时 tcls 为 NSobject, cls 依然是 NSobject ,执行判断条件 if (tcls == cls) 相等, return YES ,所以 re1 的结果为 1。
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
- re2 : 0 ,使用 +isMemberOfClass ,对比 NSObject 与 NSObject ;
- 传入的 cls 为 NSObject , self 指向 NSObject ;
- self->ISA( ) , self 的 isa 指向 NSObject meta ; NSObject meta 与 NSObject 不相等,所以 re2 的结果为 0。
BOOL re3 = [(id)[YDWPerson class] isKindOfClass:[YDWPerson class]];
- re3 : 0 ,使用 +isKindOfClass 对比 YDWPerson 与 YDWPerson;
- 传入的 cls 为 NSobject ,self 指向 Person,进入循环;
- tcls 为 YDWPerson meta ,cls 为 Person类; 执行判断条件 if (tcls == cls) ,不相等则执行 tcls = tcls->superclass ,此时 tcls 指向 NSobject metal ;
- 再次循环: tcls 为 NSobject meta ,cls 为 YDWPerson 类;不相等则执行 tcls = tcls -> superclass ,此时 tcls 指向 NSObject ;
- 第三次循环: tcls 为 NSobject , cls 为 Person类,不相等则执行 tcls = tcls->superclass ,此时 tcls 指向 nil ,不满足for循环执行条件 tcls ,所以 re3 的结果为 0。
BOOL re4 = [(id)[YDWPerson class] isMemberOfClass:[YDWPerson class]];
- re4 : 0 ,使用 +isMemberOfClass,对比 YDWPerson 类与 YDWPerson 类;
- 传入的 cls 为 YDWPerson, self 指向 YDWPerson;
- self->ISA( ) ,self 的 isa 指向 YDWPerson meta ;Person meta 与 Person 不相等,所以 re4 的结果为 0。
② 实例化方法:
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
- re5 : 1 ,使用 -isKindOfClass,对比 NSObject 对象 与 NSObject 实例;
- 传入的 cls 为 NSObject 类,self 指向 NSObject 的实例对象;
- tcls 指向 NSObject 类,cls 为 NSObject 类,执行判断 if (tcls == cls) ,相等 return YES,所以结果为 1 。
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
- re6 : 1 ,使用 -isMemberOfClass 对比 NSObject 对象 与 NSObject;
- 传入的 cls 为 NSObject, self 指向 NSObject 对象,[self class] 为 NSObject 类 ,与 cls 相等,所以 re6 的结果为 1。
BOOL re7 = [(id)[YDWPerson alloc] isKindOfClass:[YDWPerson class]];
- re7 : 1 ,使用 -isKindOfClass 对比 YDWPerson 对象 与 YDWPerson 实例;
- 传入的 cls 为 YDWPerson 类,self 指向 YDWPerson 的实例对象;
- tcls 指向 YDWPerson 类,cls 为 YDWPerson 类,执行判断 if (tcls == cls) ,相等,return YES,结束循环,所以 re7 返回 1。
BOOL re8 = [(id)[YDWPerson alloc] isMemberOfClass:[YDWPerson class]];
- re8 : 1 ,使用 -isMemberOfClass,对比 YDWPerson 对象 与 YDWPerson 实例;
- 传入的 cls 为 YDWPerson, self 指向 YDWPerson 对象,[self class] 为 YDWPerson 类 ;与 cls 相等,所以 re8 的结果为 1。
元类中为什么会有类对象的类方法?
- 在LGPerson中定义一个实例方法和一个类方法
@interface YDWPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation YDWPerson
- (void)sayHello {
NSLog(@"YDWPerson say : Hello!!!");
}
+ (void)sayHappy {
NSLog(@"YDWPerson say : Happy!!!");
}
@end
- main 主函数,用于调用自定义的方法:
int main(int argc, const char * argv[]) {
@autoreleasepool {
YDWPerson *person = [YDWPerson alloc];
Class pClass = object_getClass(person);
lgObjc_copyMethodList(pClass);
lgInstanceMethod_classToMetaclass(pClass);
lgClassMethod_classToMetaclass(pClass);
NSLog(@"Hello, World!");
}
return 0;
}
- lgObjc_copyMethodList 函数:用于获取类的方法列表
void lgObjc_copyMethodList(Class pClass){
unsigned int count = 0;
Method *methods = class_copyMethodList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Method const method = methods[i];
// 获取方法名
NSString *key = NSStringFromSelector(method_getName(method));
NSLog(@"Method, name: %@", key);
}
free(methods);
}
- lgInstanceMethod_classToMetaclass 函数:用于获取类的实例方法:
void lgInstanceMethod_classToMetaclass(Class pClass) {
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
LGLog(@"%s - %p-%p-%p-%p",__func__, method1, method2, method3, method4);
}
- lgClassMethod_classToMetaclass 函数:用于获取类的类方法:
void lgClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
LGLog(@"%s-%p-%p-%p-%p",__func__, method1, method2, method3, method4);
}
- lgIMP_classToMetaclass 函数:用于获取方法的实现:
void lgIMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
NSLog(@"%p-%p-%p-%p",imp1, imp2, imp3, imp4);
NSLog(@"%s",__func__);
}
- 函数调用的打印结果如下:
Method, name: sayHello
lgInstanceMethod_classToMetaclass - 0x1000031b0-0x0-0x0-0x100003148
lgClassMethod_classToMetaclass-0x0-0x0-0x100003148-0x100003148
0x100001d00-0x7fff6dd66580-0x7fff6dd66580-0x100001d30
lgIMP_classToMetaclass
一、lgObjc_copyMethodList 分析
lgObjc_copyMethodList 获取类中的方法列表,从实例方法存储在类中,类方法存储在元类中可得知,YDWPerson 的方法列表打印结果只有 sayHello 方法;
二、lgInstanceMethod_classToMetaclass 分析
- class_getInstanceMethod是用于获取实例方法,表示如果在传入的类或者类的父类中没有找到指定的实例方法,则返回NULL;
- method1 地址:0x1000031b0
- 传入 YDWPerson 类,获取 selName = sayHello 的实例方法;
- 在 YDWPerson 中有这个实例方法,所以会返回查找到的实例方法,所以 method1 的地址不为0x0;
- method2 地址:0x0
- 传入 YDWPerson 元类,获取 selName = sayHello 的实例方法;
- 查找顺序为元类 --> 根元类 --> 根类 --> nil,没有查到到该结果,所以 class_getInstanceMethod 返回NULL,method2 的地址为 0x0 ,表示未找到;
- method3 地址:0x0
- 传入 YDWPerson 类,获取 selName = sayHappy 的实例方法;
- 查找顺序为 YDWPerson 类 --> 根类 --> nil,没有找到 sayhello 实例方法,则返回NULL,所以 method3 的地址为 0x0 ,表示未找到;
- method4 地址:0x100003148
- 传入 YDWPerson 元类,获取 selName = sayHappy 的实例方法;
- 在 YDWPerson 元类中有 sayHappy 实例方法,这是因为类对象的类方法存储在元类中,类方法在元类中是实例方法,然后返回查找到的实例方法,所以 method3 的地址为0x100003148,表示找到了指定的实例方法;
三、lgClassMethod_classToMetaclass 分析
- class_getClassMethod主要是用于获取类方法,表示如果在传入的类或者类的父类中没有找到指定的类方法,则返回NULL;
- 源码实现如下:
// 获取类方法
Method class_getClassMethod(Class cls, SEL sel) {
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
// 获取元类
// NOT identical to this->ISA when this is a metaclass 判断是否是元类,是元类就直接返回,反之,继续找isa指向
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
-
method1 地址:0x0:
判断 YDWPerson 类是否是元类,不是则返回 YDWPerson 的元类,然后在元类中查找 sayhello 实例方法,查找顺序如下:元类 --> 根元类 --> 根类 --> nil,最后返回NULL; -
method2 地址:0x0
判断 YDWPerson 元类是否是元类,是则直接返回元类,然后在元类中查找 sayhello 实例方法,没有则返回NULL; -
method3 地址:0x100003148
判断 YDWPerson 类是否是元类,不是则返回 YDWPerson 元类,然后在元类中查找 sayHappy 实例方法,有则直接返回找到的实例方法; -
method4 地址:0x100003148
判断 YDWPerson 元类是否是元类,是则直接返回元类,然后在元类中查找 sayHappy 实例方法,有实例方法则直接返回找到的实例方法; -
从上面的分析结果中 method4 不为NULL,那么元类中为什么会有 sayHappy 类方法?这是因为 class_getClassMethod 方法在元类的判断导致的,这是苹果人为制造的递归终止条件,目的就是防止无限次递归;
四、lgIMP_classToMetaclass 分析
- class_getMethodImplementation 主要是返回方法的具体实现,表示该函数在向类实例发送消息时会被调用,并返回一个指向方法实现函数的指针,比method_getImplementation(class_getInstanceMethod(cls, name))更快,返回的函数指针可能是一个指向 runtime 内部的函数,而不一定是方法的实际实现。如果类实例无法响应selector,则返回的函数指针将是运行时消息转发机制的一部分;
- 源码实现如下:
IMP class_getMethodImplementation(Class cls, SEL sel) {
IMP imp;
if (!cls || !sel) return nil;
// 查找方法实现
imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
// 如果没有找到,则进行消息转发
if (!imp) {
return _objc_msgForward;
}
return imp;
}
- imp1 函数指针地址:0x100001d00
根据 YDWPerson 类,可以得出 YDWPerson 类中可以查找到 sayHello 的具体实现,所以返回一个 imp 函数指针的地址; - imp2 函数指针地址:0x7fff66238d80
根据类方法存储在元类中可知,sayHello 是一个实例方法,并不存储在元类中,也没有其任何实现,所以进行了消息转发; - imp3 函数指针地址:0x7fff66238d80
根据 YDWPerson 类,sayHappy 是一个类方法,并不存储在类中,也没有其任何实现,所以进行了消息转发; - imp4 函数指针地址:0x100001d30
根据类方法存储在元类文件,可以在元类中查找到 sayHappy 的具体实现,所以返回一个imp函数指针的地址;
五、结论
- class_getInstanceMethod:获取实例方法,如果指定的类或其父类不包含带有指定选择器的实例方法,则为NULL;
- class_getClassMethod:获取类方法,如果指定的类或其父类不包含具有指定选择器的类方法,则为NULL;
- class_getMethodImplementation:获取方法的具体实现,如果未查找到,则进行消息转发。
isa 指针的作用
- 对象的 isa 指向类,类的 isa 指向元类(meta class),元类isa指向元类的根类,isa 帮助一个对象找到它的方法;
- isa 是一个Class 类型的指针. 每个实例对象有个isa的指针,它指向对象的类,而 Class 里也有个 isa 的指针, 指向 meteClass(元类),元类保存了类方法的列表。
- 当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向他父类查找该方法。
- 同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的 isa 指针最终指向的是一个根元类(root meteClass)。根元类的isa指针指向本身,这样形成了一个封闭的内循环。
各种类型编码
- 定义以下类型并打印:
NSLog(@"char --> %s",@encode(char));
NSLog(@"int --> %s",@encode(int));
NSLog(@"short --> %s",@encode(short));
NSLog(@"long --> %s",@encode(long));
NSLog(@"long long --> %s",@encode(long long));
NSLog(@"unsigned char --> %s",@encode(unsigned char));
NSLog(@"unsigned int --> %s",@encode(unsigned int));
NSLog(@"unsigned short --> %s",@encode(unsigned short));
NSLog(@"unsigned long --> %s",@encode(unsigned long long));
NSLog(@"float --> %s",@encode(float));
NSLog(@"bool --> %s",@encode(bool));
NSLog(@"void --> %s",@encode(void));
NSLog(@"char * --> %s",@encode(char *));
NSLog(@"id --> %s",@encode(id));
NSLog(@"Class --> %s",@encode(Class));
NSLog(@"SEL --> %s",@encode(SEL));
int array[] = {
1,2,3};
NSLog(@"int[] --> %s",@encode(typeof(array)));
typedef struct person{
char *name;
int age;
} Person;
NSLog(@"struct --> %s",@encode(Person));
typedef union union_type{
char *name;
int a;
}Union;
NSLog(@"union --> %s",@encode(Union));
int a = 2;
int *b = {
&a};
NSLog(@"int[] --> %s",@encode(typeof(b)));
- 输出结果
char --> c
int --> i
short --> s
long --> q
long long --> q
unsigned char --> C
unsigned int --> I
unsigned short --> S
unsigned long --> Q
float --> f
bool --> B
void --> v
char * --> *
id --> @
Class --> #
SEL --> :
int[] --> [3i]
struct --> {
person=*i}
union --> (union_type=*i)
int[] --> ^i