isMemberOfClass
今天通过两个经典面试题,继续加深对isa & 继承关系 & 类结构
的理解。
在之前的isa探索中,我们了解了:isa
的指向:对象的 isa 指向 类; 类的 isa 指向 元类;元类的 isa 指向 根元类;根元类的 isa 指向 自己。类的superclass
的指向:类的 superclass 指向 父类, 父类的 superclass 指向 根类 ,根类的superclass 指向 nil;元类的superclass
的指向:元类的 superclass 指向 父类的元类,父元类的 superclass 指向 根类的元类 根元类的 superclass 指向 根类 根类的 superclass 指向 nil。
int main(int argc, const char * argv[]) { @autoreleasepool { //iskindOfClass & isMemberOfClass 类方法调用 BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; BOOL re2 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]]; BOOL re3 = [(id)[NSObject class] isMemberOfClass:[NSObject 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); //iskindOfClass & isMemberOfClass 实例方法调用 BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; BOOL re6 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]; BOOL re7 = [(id)[NSObject alloc] isMemberOfClass:[NSObject 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); } return 0; }
打印结果:
re1 :1 re2 :0 re3 :0 re4 :0 re5 :1 re6 :1 re7 :1 re8 :1
在分析实例之前我们先来看一下源码解析
//--isKindOfClass---类方法、对象方法 //+ isKindOfClass:第一次比较是 获取类的元类 与 传入类对比,再次之后的对比是获取上次结果的父类 与 传入 类进行对比 + (BOOL)isKindOfClass:(Class)cls { // 获取类的元类 vs 传入类 // 根元类 vs 传入类 // 根类 vs 传入类 // 举例:LGPerson vs 元类 (根元类) (NSObject) for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) { if (tcls == cls) return YES; } return NO; } //- isKindOfClass:第一次是获取对象类 与 传入类对比,如果不相等,后续对比是继续获取上次 类的父类 与传入类进行对比 - (BOOL)isKindOfClass:(Class)cls { /* 获取对象的类 vs 传入的类 父类 vs 传入的类 根类 vs 传入的类 nil vs 传入的类 */ for (Class tcls = [self class]; tcls; tcls = tcls->superclass) { if (tcls == cls) return YES; } return NO; }
//类方法 //+ isMemberOfClass : 获取类的元类,与 传入类对比 + (BOOL)isMemberOfClass:(Class)cls { return self->ISA() == cls; } //实例方法 //- isMemberOfClass : 获取对象的类,与 传入类对比 - (BOOL)isMemberOfClass:(Class)cls { return [self class] == cls; }
源码分析总结:
isKindOfClass
类方法:
元类 --> 根元类 --> 根类 --> nil 与 传入类的对比
实例方法:对象的类 --> 父类 --> 根类 --> nil 与 传入类的对比
isMemberOfClass
类方法:
类的元类
与传入类
对比
实例方法:对象的父类
与传入类
对比
透过上面的源码解析总结,我们分析我们的示例代码
ret1 = 1 : 传入的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。
ret2 = 0 : 传入的cls为 NSobject,self指向 Person,进入循环
第一次循环:tcls 为 Person meta,cls 为 Person类; 执行判断条件 if (tcls == cls) ,不相等,执行 tcls = tcls->superclass ,此时 tcls 指向 NSobject metal。进入第二次循环。
第二次循环: tcls 为 NSobject meta ,cls 为 Person类;不相等,执行 tcls = tcls->superclass ,此时 tcls 指向 NSObject。进入第三次循环。
第三次循环: tcls 为 NSobject ,cls 为 Person类;不相等,执行 tcls = tcls->superclass ,此时 tcls 指向 nil。不满足for循环执行条件 tcls。结束循环。
所以 re2 的结果为 0。
ret3 = 0 : 传入的cls 为 NSObject, self 指向 NSObject
self->ISA( ) ,self的 isa 指向 NSObject meta ;NSObject meta 与 NSObject 不相等。
所以 re3 的结果为 0。
ret4 = 0 : 传入的cls 为 Person, self 指向 Person
self->ISA( ) ,self的 isa 指向 Person meta ;Person meta 与 Person 不相等。
所以 re4 的结果为 0。
ret5 = 1 : 传入的cls 为 NSObject 类,self 指向 NSObject 的 实例对象
第一次循环:tcls 指向 NSObject 类,cls 为 NSObject 类,执行判断 if (tcls == cls) ,相等,return YES,结束循环。
所以 re5 返回 1。
ret6 = 1 : 传入的cls 为 Person 类,self 指向 Person 的 实例对象
第一次循环:tcls 指向 Person 类,cls 为 Person 类,执行判断 if (tcls == cls) ,相等,return YES,结束循环。
所以 re6 返回 1。
ret7 = 1 : 传入的cls 为 NSObject, self 指向 NSObject 对象
[self class] 为 NSObject 类 ;与 cls 相等。
所以 re7 的结果为 1。
ret8 = 1 : 传入的cls 为 Person, self 指向 Person 对象
[self class] 为 Person 类 ;与 cls 相等。
所以 re8 的结果为 1。
#import <Foundation/Foundation.h> @interface LGPerson : NSObject - (void)sayHello; + (void)sayHappy; @end #import "LGPerson.h" @implementation LGPerson - (void)sayHello{ NSLog(@"LGPerson say : Hello!!!"); } + (void)sayHappy{ NSLog(@"LGPerson say : Happy!!!"); } @end
int main(int argc, const char * argv[]) { @autoreleasepool { LGPerson *person = [LGPerson alloc]; Class pClass = object_getClass(person); lgObjc_copyMethodList(pClass); lgInstanceMethod_classToMetaclass(pClass); lgClassMethod_classToMetaclass(pClass); NSLog(@"Hello, World!"); } return 0; }
//获取类的方法列表 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)); LGLog(@"Method, name: %@", key); } free(methods); }
//获取类的实例方法 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); }
//获取类的类方法 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)); // 元类 为什么有 sayHappy 类方法 0 1 // Method method4 = class_getClassMethod(metaClass, @selector(sayHappy)); LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4); }
//获取方法的实现 void lgIMP_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(@"%s-%p-%p-%p-%p",__func__,imp1,imp2,imp3,imp4); }
打印结果:
Method, name: sayHello lgInstanceMethod_classToMetaclass - 0x1000031b0-0x0-0x0-0x100003148 lgClassMethod_classToMetaclass-0x0-0x0-0x100003148-0x100003148 lgIMP_classToMetaclass-0x100001d10-0x7fff66861580-0x7fff66861580-0x100001d40 2020-09-15 18:46:31.544820+0800 002-类方法归属分析[18280:408952] Hello, World!
打印结果分析:
1.lgObjc_copyMethodLists
函数里面的class_copyMethodList
函数
/** * Describes the instance methods implemented by a class. * * @param cls The class you want to inspect. * @param outCount On return, contains the length of the returned array. * If outCount is NULL, the length is not returned. * * @return An array of pointers of type Method describing the instance methods * implemented by the class—any instance methods implemented by superclasses are not included. * The array contains *outCount pointers followed by a NULL terminator. You must free the array with free(). * * If cls implements no instance methods, or cls is Nil, returns NULL and *outCount is 0. * * @note To get the class methods of a class, use \c class_copyMethodList(object_getClass(cls), &count). * @note To get the implementations of methods that may be implemented by superclasses, * use \c class_getInstanceMethod or \c class_getClassMethod. */ OBJC_EXPORT Method _Nonnull * _Nullable class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
在lgObjc_copyMethodList
这个函数中,主要是获取LGPerson类中的方法列表,从实例方法存储在类中,类方法存储在元类中可以得知,LGPerson的方法列表打印结果为sayHello方法
2.lgInstanceMethod_classToMetaclass
函数里面的class_getInstanceMethod
函数
/** * Returns a specified instance method for a given class. * * @param cls The class you want to inspect. * @param name The selector of the method you want to retrieve. * * @return The method that corresponds to the implementation of the selector specified by * \e name for the class specified by \e cls, or \c NULL if the specified class or its * superclasses do not contain an instance method with the specified selector. * * @note This function searches superclasses for implementations, whereas \c class_copyMethodList does not. */ OBJC_EXPORT Method _Nullable class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name) OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
主要是用于获取实例方法,通过开发文档苹果给出的说明是:
如果在传入的类或者类的父类中没有找到指定的实例方法,则返回NULL
3.lgClassMethod_classToMetaclass
函数里面的class_getClassMethod
函数
/** * Returns a pointer to the data structure describing a given class method for a given class. * * @param cls A pointer to a class definition. Pass the class that contains the method you want to retrieve. * @param name A pointer of type \c SEL. Pass the selector of the method you want to retrieve. * * @return A pointer to the \c Method data structure that corresponds to the implementation of the * selector specified by aSelector for the class specified by aClass, or NULL if the specified * class or its superclases do not contain an instance method with the specified selector. * * @note Note that this function searches superclasses for implementations, * whereas \c class_copyMethodList does not. */ OBJC_EXPORT Method _Nullable class_getClassMethod(Class _Nullable cls, SEL _Nonnull name) OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
//获取类方法 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(); }
class_getClassMethod
的实现是获取类的类方法
,其本质就是获取元类的实例方法
,最终还是会走到class_getInstanceMethod
,但是在这里需要注意的一点是:在getMeta源码中,如果判断出cls是元类,那么就不会再继续往下递归查找,会直接返回this,其目的是为了防止元类的无限递归查找。
4.lgIMP_classToMetaclass
函数里面的class_getMethodImplementation
函数
/** * Returns the function pointer that would be called if a * particular message were sent to an instance of a class. * * @param cls The class you want to inspect. * @param name A selector. * * @return The function pointer that would be called if \c [object name] were called * with an instance of the class, or \c NULL if \e cls is \c Nil. * * @note \c class_getMethodImplementation may be faster than \c method_getImplementation(class_getInstanceMethod(cls, name)). * @note The function pointer returned may be a function internal to the runtime instead of * an actual method implementation. For example, if instances of the class do not respond to * the selector, the function pointer returned will be part of the runtime's message forwarding machinery. */ OBJC_EXPORT IMP _Nullable class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
该函数在向类实例发送消息时会被调用,并返回一个指向方法实现函数的指针。这个函数会比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; }
class_getInstanceMethod
:获取实例方法,如果指定的类或其父类不包含带有指定选择器的实例方法,则为NULLclass_getClassMethod
:获取类方法,如果指定的类或其父类不包含具有指定选择器的类方法,则为NULL。class_getMethodImplementation
:获取方法的具体实现,如果未查找到,则进行消息转发
未完待续···不断学习,不断更新