iOS面试系列

iOS中两道经典面试题解析

2020-09-16  本文已影响0人  北京_小海

1.方法归属

自定义类一个实例方法和一个类方法

main函数调用

lgObjc_copyMethodList 函数:用于获取类的方法列表

lgInstanceMethod_classToMetaclass 函数:用于获取类的实例方法

lgClassMethod_classToMetaclass 函数:用于获取类的类方法

lgIMP_classToMetaclass 函数:用于获取方法的实现

打印结果

解析:

lgObjc_copyMethodList函数 分析

在这个函数中,主要是获取LGPerson类中的方法列表,从实例方法存储在类中,类方法存储在元类中可以得知,LGPerson的方法列表打印结果只有sayHello方法

lgInstanceMethod_classToMetaclass 函数 分析

从上面代码可知,传入的pclass 是类LGPerson,通过objc_getMetaClass获取的LGPerson的元类 是元类LGPerson,函数中4个打印结果分别是:

method1 地址:0x1000031b0

传入的pClass是LGPerson类,需要去获取selName = sayHello的实例方法

首先在LGPerson中查找,有前面的LGPerson类可知,是有这个实例方法的,所以会返回查找到的实例方法,所以method1的地址不为0x0

method2 地址:0x0

传入的pClass是LGPerson元类,需要去获取selName = sayHello的实例方法

其查找的顺序为元类 --> 根元类 --> 根类 --> nil,直到最后也没有找到,所以class_getInstanceMethod返回NULL,其method2的地址为0x0,表示未找到

method3 地址:0x0

传入的pClass是LGPerson类,需要去获取selName = sayHappy的实例方法

查找顺序为LGPerson类 --> 根类 --> nil,也没有找到sayHappy实例方法,返回NULL,所以method3的地址为0x0,表示未找到

method4 地址:0x100003148

传入的pClass是LGPerson元类,需要去获取selName = sayHappy的实例方法

首先在LGPerson元类中查找,发现有sayHappy的实例方法,主要是因为类对象的类方法存储在元类中,类方法在元类中是实例方法,然后返回查找到的实例方法,所以method3的地址为0x100003148,表示找到了指定的实例方法

lgClassMethod_classToMetaclass函数分析

class_getClassMethod


看该方法的源码实现,可以得出class_getClassMethod的实现是获取类的类方法,其本质就是获取元类的实例方法,最终还是会走到class_getInstanceMethod,但是在这里需要注意的一点是:在getMeta源码中,如果判断出cls是元类,那么就不会再继续往下递归查找,会直接返回this,其目的是为了防止元类的无限递归查找

打印结果有以下分析:

method1 地址:0x0

pClass是LGPerson类,selName是sayHello

首先判断LGPerson类是否是元类,此时不是,返回LGPerson的元类,然后在元类中查找sayhello实例方法。查找顺序如下:元类 --> 根元类 --> 根类 --> nil,最后返回NULL

method2 地址:0x0

metaClass是LGPerson元类,selName是sayHello

首先判断LGPerson元类是否是元类,此时是,直接返回元类,然后在元类中查找sayhello实例方法,发现并没有找到,返回NULL

method3 地址:0x100003148

pClass是LGPerson类,selName是sayHappy

首先判断LGPerson类是否是元类,此时不是,返回LGPerson的元类,然后在元类中查找sayHappy实例方法,发现有这个实例方法,直接返回找到的实例方法。

method4 地址:0x100003148

pClass是LGPerson元类,selName是sayHappy

首先判断LGPerson元类是否是元类,此时是,直接返回元类,然后在元类中查找sayHappy实例方法,发现有这个实例方法,直接返回找到的实例方法

lgIMP_classToMetaclass函数 分析

该函数在向类实例发送消息时会被调用,并返回一个指向方法实现函数的指针。这个函数会比method_getImplementation(class_getInstanceMethod(cls, name))更快。返回的函数指针可能是一个指向runtime内部的函数,而不一定是方法的实际实现。如果类实例无法响应selector,则返回的函数指针将是运行时消息转发机制的一部分

class_getMethodImplementation


4个打印结果:

imp1 函数指针地址:0x100001d00

pClass 是 LGPerson类,sel 是 sayHello

根据LGPerson文件,可以得出LGPerson类中可以查找到sayHello的具体实现,所以返回一个imp函数指针的地址

imp2 函数指针地址:0x7fff66238d80

pClass是LGPerson元类,sel是sayHello

根据类方法存储在元类中可知,sayHello是一个实例方法,并不存储在元类中,也没有其任何实现,所以进行了消息转发

mp3 函数指针地址:0x7fff66238d80

pClass是LGPerson类,sel是sayHappy

根据LGPerson文件,sayHappy是一个类方法,并不存储在类中,也没有其任何实现,所以进行了消息转发

imp4 函数指针地址:0x100001d30

pClass是LGPerson元类,sel是sayHappy

根据类方法存储在元类文件,可以在元类中查找到sayHappy的具体实现,所以返回一个imp函数指针的地址

总结

class_getInstanceMethod:获取实例方法,如果指定的类或其父类不包含带有指定选择器的实例方法,则为NULL

class_getClassMethod:获取类方法,如果指定的类或其父类不包含具有指定选择器的类方法,则为NULL。

class_getMethodImplementation:获取方法的具体实现,如果未查找到,则进行消息转发

2.isKindOfClass 和 isMemberOfClass

先上问题和答案~~

先分析方法源码

isKindOfClass

类方法:元类 --> 根元类 --> 根类 --> nil 与 传入类的对比

实例方法:对象的类 --> 父类 --> 根类 --> nil 与 传入类的对比

isMemberOfClass

类方法: 类的元类 与 传入类 对比

实例方法:对象的父类 与 传入类 对比

根据源码的分析,来分析代码执行结果

re1 :1 ,是NSObject与NSObject的对比,使用+isKindOfClass

NSObject(传入类,即根类) vs NSObject的元类即根元类-- 不相等

NSObject(传入类,即根类) vs 根元类的父类即根类-- 相等,返回1

re2 :0 ,是NSObject与NSObject的对比,使用+isMemberOfClass

NSObject根类(传入类) vs NSObject的元类即根元类-- 不相等

re3 :0 ,是LGPerson与LGPerson的对比,使用+isKindOfClass

LGPerson(传入类) vs LGPerson的元类即元类LGPerson  -- 不相等

LGPerson(传入类) vs 元类LGPerson的父类即根元类-- 不相等

LGPerson(传入类) vs 根元类的父类即根类-- 不相等

LGPerson(传入类) vs 根类的父类即nil-- 不相等

re4 :0 ,是LGPerson与LGPerson的对比,使用+isMemberOfClass

LGPerson(传入类) vs元类-- 不相等

使用实例方法结果解析

re5 :1 ,是NSObject对象与NSObject的对比,使用-isKindOfClass

NSObject(传入类,即根类) vs 对象的类即NSObject根类 -- 相等

re6 :1 ,是NSObject对象与NSObject的对比,使用-isMemberOfClass

NSObject(传入类,即根类) vs 对象的类即NSObject根类 -- 相等

re7 :1 ,是LGPerson对象与LGPerson的对比,使用-isKindOfClass

LGPerson(传入类) vs 对象的类即LGPerson -- 相等

re8 :1 ,是LGPerson对象与LGPerson的对比,使用-isMemberOfClass

LGPerson(传入类) vs 对象的类即LGPerson -- 相等

上一篇下一篇

猜你喜欢

热点阅读