美文网首页
iOS类面试题分析

iOS类面试题分析

作者: Bel李玉 | 来源:发表于2020-09-17 00:14 被阅读0次

这篇文章主要是从源码的角度分析 class_getInstanceMethod方法和class_getClassMethod方法的实现, isKindOfClassisMemberOfClass方法的实现。

class_getInstanceMethod VS class_getClassMethod

class_getInstanceMethod

首先我们先定义一个Person类

NS_ASSUME_NONNULL_BEGIN
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;

@end
NS_ASSUME_NONNULL_END
@implementation LGPerson

- (void)sayHello{
    NSLog(@"LGPerson say : Hello!!!");
}

+ (void)sayHappy{
    NSLog(@"LGPerson say : Happy!!!");
}
@end

我们看下定义如下函数

void lgInstanceMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className); // 元类
    // 类对象的methods存放的是实例方法,能找到
    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
    // 元类对象的methods存放的是类方法,找不到
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
   // 类对象的methods存放的是实例方法, 找不到
    Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
   // 元类对象的methods存放的是类方法,能找到
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        LGPerson *person = [LGPerson alloc];
        Class pClass     = object_getClass(person);
        // 调用函数
        lgInstanceMethod_classToMetaclass(pClass); 
        NSLog(@"Hello, World!");
    }
    return 0;
}

我们先来看下输出结果

lgInstanceMethod_classToMetaclass - 0x1000031b0-0x0-0x0-0x100003148

从输出结果我们可知:method1 ,method4有值,method2,method3没有值

我们先看下class_getInstanceMethod的源码,关键代码


// 1
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
    runtimeLock.assertLocked();

    ASSERT(cls->isRealized());
    // fixme nil cls? 
    // fixme nil sel?

    auto const methods = cls->data()->methods();
    for (auto mlists = methods.beginLists(),
              end = methods.endLists();
         mlists != end;
         ++mlists)
    {
        // <rdar://problem/46904873> getMethodNoSuper_nolock is the hottest
        // caller of search_method_list, inlining it turns
        // getMethodNoSuper_nolock into a frame-less function and eliminates
        // any store from this codepath.
        method_t *m = search_method_list_inline(*mlists, sel);
        if (m) return m;
    }

    return nil;
}
// 2
static method_t *
getMethod_nolock(Class cls, SEL sel)
{
    method_t *m = nil;

    runtimeLock.assertLocked();

    // fixme nil cls?
    // fixme nil sel?

    ASSERT(cls->isRealized());

    while (cls  &&  ((m = getMethodNoSuper_nolock(cls, sel))) == nil) {
        cls = cls->superclass;
    }

    return m;
}

类的结构分析文章中,我们可知,instanceMethod存在于类对象methods里面。

  • 1,auto const methods = cls->data()->methods();当前cls类对象 从这里我们看出,先从类对象method_list里面进行查找。
  • 2,如果在类对象里,没有找到,在到 父类的类对象method_list进行查找
    由此我们可以得出,class_getInstanceMethod,会首先在类对象methods里面查找该方法,如果没有找到,会在父类(super class)里面进行查找。
    如果传入的类是 元类元类methods存放的是类的类方法
class_getClassMethod
void lgClassMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    // 0 0
    // 在 元类的 method()里面查找,找不到实例方法
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
  // 在 元类的 method()里面查找,找不到实例方法
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
    // 在 元类的 method()里面查找,能找到类方法
    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
   // 在 元类的 method()里面查找,能找到类方法
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *person = [LGPerson alloc];
        Class pClass     = object_getClass(person);
        lgClassMethod_classToMetaclass(pClass);
    }
    return 0;
}

输出结果如下:

lgClassMethod_classToMetaclass-0x0-0x0-0x100003148-0x100003148

我们看下源码实现 class_getClassMethod

Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}

 Class getMeta() {
        // 如果是 元类,就返回自身,防止 getMeta无限递归
        if (isMetaClass()) return (Class)this;
        else return this->ISA();// 返回元类对象
    }

从源码我们可以看出:

  • 1,查找类对象类方法,就是查找元类的实例方法,在 元类methods()里面查找。元类对象methods()保存的是类方法
isKindOfClass VS isMemberOfClass

我们先看下如下函数的输出结果

        BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];       //
        BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];     //
        BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];       //
        BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];     //
        NSLog(@" re1 :%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)[LGPerson alloc] isKindOfClass:[LGPerson class]];       //
        BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];     //
        NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);

输出结果如下

2020-09-16 22:49:45.102047+0800 KCObjc[3562:3404422]  re1 :1
 re2 :0
 re3 :0
 re4 :0
2020-09-16 22:49:45.108914+0800 KCObjc[3562:3404422]  re5 :1
 re6 :1
 re7 :1
 re8 :1
Program ended with exit code: 0

我们依次来分析这四个函数

1,+ (BOOL)isKindOfClass:(Class)cls:
其源码实现如下

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}
  • cls类对象,则 tcls元类对象,判断是否相等,如果不相等,则 tcls = metaSuperclass,tcls值为元类,根元类或NSObject,到NSObject为止,因为 NSObjectsuperClassnil,tclsnil会终止循环。

我们来分析 ret1和ret3的结果:

BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];  // 1
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];  // 2
  • 1,首先,NSObject的元类是不等于NSObject的类对象NSObject的元类superClassNSObject类对象,所以,re1 == 1
  • 2,LGPerson的元类不等于 LGPerson 的类对象元类superclass还是元类根元类superclassNSObject,所以不会等于 LGPerson类对象,所以 , re2 == 0

2,+ (BOOL)isMemberOfClass:(Class)cls
我们看下其源码实现

+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}
  • 只判断一次,判断 isa 是不是指向 cls

我们来分析 re2和re4的结果

BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; // 1
BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]]; // 2
  • [NSObject class]isa指向 NSObject元类,不等于其类对象,re2 = 0
  • [LGPerson class]isa指向 LGPerson元类,不等于其类对象,re3 = 0

3,- (BOOL)isKindOfClass:(Class)cls
其源码实现如下

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}
  • 先判断其类对象是否与 cls相等,如果不相等,就依次比较其类对象的父类

我们来分析 re5 和 re7

BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; // 1
BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]; // 2
  • 1, NSObject的类对象 == NSObject的类对象 ,所以 re5的值为1。
  • 2,LGPerson的类对象 == LGPerson的类对象,所以re7的值为1。

4,- (BOOL)isMemberOfClass:(Class)cls
其源码实现如下:

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
  • 只对其类对象比较一次。

我们对剩下的最后两个 re6 和 re8 进行分析

BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; \\ 1
BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]]; \\ 2
  • 通过对该函数的分析,两者都是相等的,所以 re6 == 1,re8 == 1。

总结:

class_getInstanceMethod:函数会 在其类对象method()里面查找,类对象里面存放的实例方法

class_getClassMethod: 函数,无论该类是 类对象还是元类对象,都在元类method()里面查找,元类method()里面存放的都是类方法。

isKindOfClass: 1,其实例方法,会循环判断其类对象类对象的父类是否和传入类是否相等。2,其类方法,会 循环判断其元类元类的父类是否和传入类是否相等。

isMemberOfClass: 1,其实例方法,只会判断一次,当前类对象是否等于传入类。2,其类方法,只会判断一次,判断传入类是不是其元类

相关文章

网友评论

      本文标题:iOS类面试题分析

      本文链接:https://www.haomeiwen.com/subject/qubqyktx.html