前言
之前我们学习了类的相关知识和isa走位,为了加深印象,接下来我们通过两个例子来复习一下,这两个例子也是一下大厂可能出现的面试题
一、isKindOfClass和isMemberOfClass
void testISATachnology(){
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[WJPerson class] isKindOfClass:[WJPerson class]];
BOOL re4 = [(id)[WJPerson class] isMemberOfClass:[WJPerson 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)[WJPerson alloc] isKindOfClass:[WJPerson class]];
BOOL re8 = [(id)[WJPerson alloc] isMemberOfClass:[WJPerson class]];
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
}
大家知道上面这段代码打印的结果是什么吗?
我们先来分析下题目,从上面代码中可以看到前4行代码调用的都是类方法,后4行都是对象方法。
我们先来分析一下+ (BOOL)isKindOfClass:(Class)cls
类方法,我们看下苹果的源码实现
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
从上述代码中可以看出先找到本身的元类所以tcls
为当前类的元类,然后用tcls
与tcls
的父类对比,如果一样则返回YES
。
我们先来分析下res1
的结果
-
tcls
为NSObject
的元类也就是根元类
,cls
为NSObject
也就是根类
得出结论tcls != cls
,继续往下走,tcls
为根元类
的父类也就是根类
,此时tcls == cls
,返回为YES
。
我们再来分析下res3
的结果
-
tcls
为WJPerson
的元类,cls
为WJPerson
类
得出结论tcls != cls
,继续往下走,tcls
为WJPerson
元类的父类,也就是根元类
。
得出结论tcls != cls
,继续往下走,tcls
为根元类
的父类也就是根类
。
得出结论tcls != cls
,继续往下走,tcls
为根类
的父类为nil
。
得出结论tcls != cls
,此时跳出循环,返回NO
。
通过上面的分析我们可以得出结论:
如果指定类是当前类的元类的父类则返回YES,否则返回NO
。
我们再来分析一下- (BOOL)isKindOfClass:(Class)cls
对象方法,我们看下苹果的源码实现
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
从上述代码中可以得出结论:
如果当前对象的类是指定类或其子类则返回YES,否则返回NO
。
从题目中得知re5
、re7
中都是当前对象的类
==指定类
所以都返回为YES
。
接下来我们分析一下+ (BOOL)isMemberOfClass:(Class)cls
这个类方法
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
从上述代码中可以得出结论:
如果指定类是当前类的元类,则返回YES,否则返回NO
。
从题目中得知re2
、re4
中都是指定类
==当前类
,所以都返回为NO
。
最后我们分析一下- (BOOL)isMemberOfClass:(Class)cls
这个对象方法
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
从上述代码中可以得出结论:
如果当前对象的类是指定类则返回YES,否则返回NO
。
从题目中得知re6
、re8
中都是当前对象的类
==指定类
,所以都返回为YES
。
虽有我们可以推导出打印结果为 1 0 0 0 1 1 1 1
。
我们看下实际打印结果
2020-09-15 14:59:39.598314+0800 KCObjc[2299:91882] re1 :1
re2 :0
re3 :0
re4 :0
2020-09-15 14:59:39.601873+0800 KCObjc[2299:91882] re5 :1
re6 :1
re7 :1
re8 :1
总结
-
+ (BOOL)isKindOfClass:(Class)cls
:如果cls
是当前类的元类的父类则返回YES,否则返回NO。 -
- (BOOL)isKindOfClass:(Class)cls
:如果当前对象的类是cls
或其子类则返回YES,否则返回NO。 -
+ (BOOL)isMemberOfClass:(Class)cls
:如果cls
是当前类的元类,则返回YES,否则返回NO。 -
- (BOOL)isMemberOfClass:(Class)cls
:如果当前对象的类是cls
则返回YES,否则返回NO。
二、类的方法查找
@interface WJPerson : NSObject
- (void)sayHello;
+ (void)sayGoodbye;
@end
@implementation WJPerson
- (void)sayHello{
NSLog(@"%s",__func__);
}
+ (void)sayGoodbye{
NSLog(@"%s",__func__);
}
@end
我们先在类中定义一个对象方法,一个类方法。然后判断下面几段代码的打印结果。
一:class_getInstanceMethod探索
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(sayGoodbye));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayGoodbye));
NSLog(@"%s - %d-%d-%d-%d",__func__,method1!=nil,method2!=nil,method3!=nil,method4!=nil);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
WJPerson *person = [WJPerson alloc];
Class pClass = object_getClass(person);
instanceMethod_classToMetaclass(pClass);
}
return 0;
}
我们先看下class_getInstanceMethod
的源码实现
/***********************************************************************
* class_getInstanceMethod. Return the instance method for the
* specified class and selector.
**********************************************************************/
Method class_getInstanceMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
// This deliberately avoids +initialize because it historically did so.
// This implementation is a bit weird because it's the only place that
// wants a Method instead of an IMP.
#warning fixme build and search caches
// Search method lists, try method resolver, etc.
lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);
#warning fixme build and search caches
return _class_getMethod(cls, sel);
}
由方法的注释可以知道class_getInstanceMethod
的作用是获取指定类和指定sel的实例方法
。
这里pClass
是WJPerson
类,meteClass
是WJPerson
的元类。
通过iOS底层之类结构分析这篇文章我们得知类中存放的是类的实例方法
,元类中存放的是类的类方法,注:类的类方法也可以叫做元类的实例方法
。所以我们可以得出结论method1
和method4
不为空,其它为空,所以推导出打印结果为 1 0 0 1
。
看下实际打印结果
instanceMethod_classToMetaclass - 1-0-0-1
二、class_getClassMethod探索
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(sayGoodbye));
//
Method method4 = class_getClassMethod(metaClass, @selector(sayGoodbye));
NSLog(@"%s - %d-%d-%d-%d",__func__,method1!=nil,method2!=nil,method3!=nil,method4!=nil);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
WJPerson *person = [WJPerson alloc];
Class pClass = object_getClass(person);
classMethod_classToMetaclass(pClass);
}
return 0;
}
我们先看下class_getClassMethod
的源码实现
/***********************************************************************
* class_getClassMethod. Return the class method for the specified
* class and selector.
**********************************************************************/
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
/**
* // NOT identical to this->ISA when this is a metaclass
* Class getMeta() {
* if (isMetaClass()) return (Class)this;
* else return this->ISA();
* }
* 通过查看getMeta()实现发现,如果当前cls为元类则返回自身
*/
return class_getInstanceMethod(cls->getMeta(), sel);
}
由方法的注释可以知道class_getClassMethod
的作用是获取指定类和指定sel的类方法
。但是进一步观察发现实际上是获取指定类的元类和指定sel的实例方法
.
这里pClass
是WJPerson
类,meteClass
是WJPerson
的元类。
-
method1
是获取WJPerson
的元类的sayHello
方法,而sayHello
为是WJPerson
类的实例方法,所以获取不到,method1为nil
,同理method2也为nil
。 -
method3
是获取WJPerson
的元类的sayGoodbye
方法,sayGoodbye
是WJPerson
的元类的实例方法,所以method3不为nil
,同理method4也不为nil
。
所以我们可以得出结论method3
和method4
不为空,其它为空,所以推导出打印结果为0 0 1 1
。
看下实际打印结果
classMethod_classToMetaclass - 0-0-1-1
三、class_getMethodImplementation探索
void iMP_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(sayGoodbye));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayGoodbye));
NSLog(@"%s - %d-%d-%d-%d",__func__,imp1!=nil,imp2!=nil,imp3!=nil,imp4!=nil);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
WJPerson *person = [WJPerson alloc];
Class pClass = object_getClass(person);
iMP_classToMetaclass(pClass);
}
return 0;
}
我们先看下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.
*/
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;
}
由方法的注释可以知道class_getMethodImplementation
的作用是获取指定类的指定方法实现
。
这里pClass
是WJPerson
类,meteClass
是WJPerson
的元类。
-
imp1
是获取WJPerson
的类的sayHello
方法实现,sayHello
为是WJPerson
类的实例方法,且已经进行了实现,所以可以获取到,imp1为-[WJPerson sayHello]
。 -
imp2
是获取WJPerson
的元类的sayHello
方法实现,而sayHello
为是WJPerson
类的实例方法,虽然进行了实现,但并不能在WJPerson
的元类中查找到实现,这是会进入消息转发流程
,返回的是libobjc.A.dylib _objc_msgForward
。 -
imp3
是获取WJPerson
类的sayGoodbye
方法,sayGoodbye
是WJPerson
的元类的实例方法,虽然进行了实现,但并不能在WJPerson
的类中查找到实现,此时会进入消息转发流程
,返回的是libobjc.A.dylib _objc_msgForward
。 -
imp1
是获取WJPerson
的元类的sayGoodbye
方法实现,sayGoodbye
为是WJPerson
的元类的实例方法,且已经进行了实现,所以可以获取到,imp4为+[WJPerson sayGoodbye]
。
所以我们可以得出结论imp1
到imp4
都不为空,所以推导出打印结果为1 1 1 1
。
看下实际打印结果
iMP_classToMetaclass - 1-1-1-1