Objective-C底层面试题总结
方法的归属问题探索
- 定一个Person类,定义一个实例方法,一个类方法,并完成实现
@interface Person : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation Person
- (void)sayHello{
NSLog(@"Person say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"Person say : Happy!!!");
}
@end
- 获取类的方法并打印出来
void objc_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);
}
- 获取实例方法并打印其地址
void instanceMethod_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));
NSLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
- 获取类方法并打印其地址
void classMethod_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));
// 元类 为什么有 sayHappy 类方法 0 1
//
Method method4 = class_getClassMethod(metaClass, @selector(esd));
NSLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
- 获取方法实现
void IMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
// - (void)sayHello;
// + (void)sayHappy;
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__);
}
- 方法调用
Person *person = [Person alloc];
Class pClass = object_getClass(person);
objc_copyMethodList(pClass);
instanceMethod_classToMetaclass(pClass);
classMethod_classToMetaclass(pClass);
IMP_classToMetaclass(pClass);
- 执行结果
Method, name: sayHello
instanceMethod_classToMetaclass - 0x1000081b0-0x0-0x0-0x100008148
classMethod_classToMetaclass-0x0-0x0-0x100008148-0x0
IMP_classToMetaclass-0x100003d10-0x7fff67dc7bc0-0x7fff67dc7bc0-0x100003d40
接下来我们逐个函数进行分析,然后论证结果
objc_copyMethodList
-
class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount)
runtime提供的函数,用来获取类的方法列表,并返回方法个数。
从这个方法里面我们知道,Person
中只存储了实例方法
class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
-
command + shift + 0先查看官方文档
class_getInstanceMethod文档.png
从文档中我们可以看出,当查找实例方法的时候,会沿着当前类的继承链找下去。
- method1
- 在
Person
类中找sayHello
实例方法,能找到,所以地址有值
- 在
- method2
- 在
MetaClass(Person)
的中找sayHello
实例方法,找不到 - 然后沿着
MetaClass→SuperMetaClass→RootMetaClass→NSObject→nil
继承链找,直到nil
都没找到sayHello
实例方法,所以打印method2的地址为0x0
- 在
- method3
- 在
Person
类中找sayHappy
类方法,没有找到 - 然后沿着
Person→NSObject→nil
继承链找,直到nil都没找到sayHappy
类方法,所以答应method2的地址为0x0
- 在
- method4
- 在
MetaClass(Person)
的中找sayHappy
类方法,能找到,所以打印的地址有值
- 在
class_getClassMethod(Class _Nullable cls, SEL _Nonnull name)
-
先看看文档怎么说
class_getClassMethod文档.png
从文档上我们可以看出,也是通过父类是否有class method来判断
-
class_getClassMethod
源码
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
看了源码,其实是去获取元类的实例化方法。如果当前类是元类就直接使用当前类,来完成
class_getInstanceMethod
,这里不继续通过→ISA()来继续往下摸,是因为会产生无限循环。我们知道class_getInstanceMethod
会根据继承链一直去寻找实例方法。
- method1:
- 首先获取
Person
的元类MetaClass(Person)
,然后寻找沿着MetaClass→SuperMetaClass→RootMetaClass→NSObject→nil
去寻找sayhello
类的实例方法,最终都找不到,所以地址为0x0
。
- 首先获取
- method2:
- 首先获取
MetaClass(Person)
的元类,因为MetaClass(Person)
已经是元类了,所以直接返回MetaClass(Person)
,然后寻找沿着MetaClass→SuperMetaClass→RootMetaClass→NSObject→nil
去寻找sayhello
类的实例方法,最终都找不到,所以地址为0x0
。
- 首先获取
- method3:
- 首先获取
Person
的元类MetaClass(Person)
,然后寻找沿着MetaClass→SuperMetaClass→RootMetaClass→NSObject→nil
去寻找Person
的元类继承链的sayHappy
实例方法,通过前面的分析我们知道Person
的类方法sayHappy
就存在MetaClass(Person)
中,所以返回打印的地址有值。
- 首先获取
- method4:
- 首先获取
MetaClass(Person)
的元类,因为MetaClass(Person)
已经是元类了,所以直接返回MetaClass(Person)
,然后寻找沿着MetaClass→SuperMetaClass→RootMetaClass→NSObject→nil
去寻方法名为sayHappy
的方法,通过前面的分析我们知道sayHappy
就存在MetaClass(Person)
中,所以返回打印的地址有值。
- 首先获取
class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name)
-
class_getMethodImplementation文档
class_getMethodImplementation文档.png
- 该方法会返回一个
指向方法实现的函数指针
- 速度比
method_getImplementation(class_getInstanceMethod(cls, name))
快 - 返回的
函数指针
不一定是该方法的实现
,也可能是runtime
的一个内部函数
- 如果
类实例
无法响应selector
,则返回的函数指针
将是runtime消息转发机制
的一部分
- 该方法会返回一个
- 再来看看源码
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
// Translate forwarding function to C-callable external version
if (!imp) {
return _objc_msgForward;
}
return imp;
}
通过源码,首先去查找方法实现,如果没有找到,则进行消息转发。
- imp1:
- 通过前面我们知道,
sayhello
实例方法,存在Person类中,能找到,所以返回函数指针,能打印出其地址
- 通过前面我们知道,
- imp2:
-
sayhello
不存在元类中,所以进行了消息转发
-
- imp3:
-
sayHappy
是类方法,不存在类中,所以进行了消息转发
-
- imp4:
-
sayhappy
是类方法,存在元类中,能在元类中找到,所以返回函数指针,能打印出其地址
-
iskindOfClass & isMemberOfClass
- 类方法
isKindOfClass
和isMemberOfClass
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[Person class] isKindOfClass:[Person class]];
BOOL re4 = [(id)[Person class] isMemberOfClass:[Person class]];
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
输出如下
re1 :1
re2 :0
re3 :0
re4 :0
- 实例方法
isKindOfClass
和isMemberOfClass
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[Person alloc] isKindOfClass:[Person class]];
BOOL re8 = [(id)[Person alloc] isMemberOfClass:[Person class]];
输出:
re5 :1
re6 :1
re7 :1
re8 :1
为什么会是这个结果,我们来分析一下
isKindOfClass
和isMemberOfClass
的源码
- isKindOfClass
-
isKindOfClass
类方法源码
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
通过源码我们可以看到,当
isKindOfClass
作为类方法的时候,是先通过ISA
获取当前类的元类,然后递归当前的元类继承链和当前类进行对比。
- re1:
-
NSObject
→ISA():RootMetaClass
VSNSObject
,不满足,循环继续 -
根元类
的superclass
是NSObject
VSNSObject
。返回YES
-
- re3
-
Peson
→ISA():MetaClass(Person)
VSPeson
,不满足,循环继续 -
Meta(Person)
→superclass:SuperMetaClass(Peson)
VSPerson
, 不满足,循环继续 -
SuperMeta(Peson)
→superclass:RootMetaClass
VSPerson
, 不满足,循环继续 -
RootMetaClass
→superclass:NSObject
VSPerson
, 不满足,循环继续 -
NSObject
→superclass:nil
,跳出循环,返回NO
-
-
isKindOfClass
实例方法源码
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
// class的实例方法
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
objc_object::getIsa()
{
if (fastpath(!isTaggedPointer())) return ISA();
extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
uintptr_t slot, ptr = (uintptr_t)this;
Class cls;
slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
cls = objc_tag_classes[slot];
if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
cls = objc_tag_ext_classes[slot];
}
return cls;
}
通过源码,当
isKindOfClass
作为实例方法的时候,先获取当前实例对象的isa
即当前实例的类,与传入类进行对比,然后再把传入类与当前实例对象的类的继承链
来进行对比
- re5:
-
[NSObject alloc]
→getIsa() :NSObject
VSNSObject
,返回YES
-
- re7:
-
[Person alloc]
→getIsa() :Person VS
VSPerson
,返回YES。
-
!!!注意这里有坑点
看上去分析的很对,实际上是这样的么?我们进行断点调试的时候发现isKindOfClass不走我们上面分析的两个方法,而是统一走objc_opt_isKindOfClass
![](https://img.haomeiwen.com/i11287969/2fafbf19a2dfb227.png)
😢接着我们来分析
objc_opt_isKindOfClass
的源码
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
Class cls = obj->getIsa();
if (fastpath(!cls->hasCustomCore())) {
for (Class tcls = cls; tcls; tcls = tcls->superclass) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
看到源码,是不是和上面分析的是基本一致的,首先通过需要判断的对象或者类的isa来获取当对象的类或者类的元类,然后通过他们的继承链来与需要对比的类进行对比
Tips:类通过isa可以获取元类,对象通过isa获取实例当前对象的类
-
isMemberOfClass
为了避免刚刚的问题,我们首先来看下isMemberOfClass方法的调用走哪个方法
isMemberOfClass方法流程.png
确实会走isMemberOfClass的底层方法,可以放心分析了 -
isMemberOfClass
类方法的源码
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
通过源码,拿元类和传入类来进行对比
- r2:
-
NSObject→ISA()
:RootMetaClass
VSNSObject
,返回NO
-
- r4:
-
Person→ISA()
:MetaClass(Person)
VSNSObject
,返回NO
-
-
isMemberOfClass
实例方法的源码
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
// class的实例方法
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
objc_object::getIsa()
{
if (fastpath(!isTaggedPointer())) return ISA();
extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
uintptr_t slot, ptr = (uintptr_t)this;
Class cls;
slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
cls = objc_tag_classes[slot];
if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
cls = objc_tag_ext_classes[slot];
}
return cls;
}
分析源码,
[self class]
其实就是通过isa
去获取实例化当前对象的类,然后与传入类来进行对比。
- r6:
-
[NSObject alloc]→getIsa()
:NSObject
VSNSObject
,返回YES
-
- r4:
-
[Person alloc]→getIsa()
:Person
VSPerson
,返回YES
-