前言
之前我們學習了類的相關知識和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