当前位置: 首页 > >

iOS经典面试题之深入解析类Class的iskindOfClass与isMemberOfClass的底层原理

发布时间:

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(@"
re1 : %hhd
re2 : %hhd
re3 : %hhd
re4 : %hhd
", 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(@"
re5 : %hhd
re6 : %hhd
re7 : %hhd
re8 : %hhd
", 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



友情链接: