iOS之深入解析类Class相关经典面试题

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

猜你喜欢

转载自blog.csdn.net/Forever_wj/article/details/108612460