方法的歸屬問(wèn)題探索
- 定一個(gè)Person類碉渡,定義一個(gè)實(shí)例方法,一個(gè)類方法母剥,并完成實(shí)現(xiàn)
@interface Person : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation Person
- (void)sayHello{
NSLog(@"Person say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"Person say : Happy!!!");
}
@end
- 獲取類的方法并打印出來(lái)
void objc_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);
}
- 獲取實(shí)例方法并打印其地址
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(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
NSLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
- 獲取類方法并打印其地址
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(sayHappy));
// 元類 為什么有 sayHappy 類方法 0 1
//
Method method4 = class_getClassMethod(metaClass, @selector(esd));
NSLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
- 獲取方法實(shí)現(xiàn)
void IMP_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(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
NSLog(@"%s",__func__);
}
- 方法調(diào)用
Person *person = [Person alloc];
Class pClass = object_getClass(person);
objc_copyMethodList(pClass);
instanceMethod_classToMetaclass(pClass);
classMethod_classToMetaclass(pClass);
IMP_classToMetaclass(pClass);
- 執(zhí)行結(jié)果
Method, name: sayHello
instanceMethod_classToMetaclass - 0x1000081b0-0x0-0x0-0x100008148
classMethod_classToMetaclass-0x0-0x0-0x100008148-0x0
IMP_classToMetaclass-0x100003d10-0x7fff67dc7bc0-0x7fff67dc7bc0-0x100003d40
接下來(lái)我們逐個(gè)函數(shù)進(jìn)行分析滞诺,然后論證結(jié)果
objc_copyMethodList
-
class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount)
runtime提供的函數(shù),用來(lái)獲取類的方法列表环疼,并返回方法個(gè)數(shù)铭段。
從這個(gè)方法里面我們知道,Person
中只存儲(chǔ)了實(shí)例方法
class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
-
command + shift + 0先查看官方文檔
從文檔中我們可以看出秦爆,當(dāng)查找實(shí)例方法的時(shí)候,會(huì)沿著當(dāng)前類的繼承鏈找下去憔披。
- method1
- 在
Person
類中找sayHello
實(shí)例方法等限,能找到,所以地址有值
- 在
- method2
- 在
MetaClass(Person)
的中找sayHello
實(shí)例方法芬膝,找不到 - 然后沿著
MetaClass→SuperMetaClass→RootMetaClass→NSObject→nil
繼承鏈找望门,直到nil
都沒(méi)找到sayHello
實(shí)例方法,所以打印method2的地址為0x0
- 在
- method3
- 在
Person
類中找sayHappy
類方法锰霜,沒(méi)有找到 - 然后沿著
Person→NSObject→nil
繼承鏈找筹误,直到nil都沒(méi)找到sayHappy
類方法,所以答應(yīng)method2的地址為0x0
- 在
- method4
- 在
MetaClass(Person)
的中找sayHappy
類方法癣缅,能找到厨剪,所以打印的地址有值
- 在
class_getClassMethod(Class _Nullable cls, SEL _Nonnull name)
-
先看看文檔怎么說(shuō)
從文檔上我們可以看出,也是通過(guò)父類是否有class method來(lái)判斷
-
class_getClassMethod
源碼
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
看了源碼友存,其實(shí)是去獲取元類的實(shí)例化方法祷膳。如果當(dāng)前類是元類就直接使用當(dāng)前類,來(lái)完成
class_getInstanceMethod
屡立,這里不繼續(xù)通過(guò)→ISA()來(lái)繼續(xù)往下摸直晨,是因?yàn)闀?huì)產(chǎn)生無(wú)限循環(huán)。我們知道class_getInstanceMethod
會(huì)根據(jù)繼承鏈一直去尋找實(shí)例方法膨俐。
- method1:
- 首先獲取
Person
的元類MetaClass(Person)
勇皇,然后尋找沿著MetaClass→SuperMetaClass→RootMetaClass→NSObject→nil
去尋找sayhello
類的實(shí)例方法,最終都找不到焚刺,所以地址為0x0
敛摘。
- 首先獲取
- method2:
- 首先獲取
MetaClass(Person)
的元類,因?yàn)?code>MetaClass(Person)已經(jīng)是元類了檩坚,所以直接返回MetaClass(Person)
着撩,然后尋找沿著MetaClass→SuperMetaClass→RootMetaClass→NSObject→nil
去尋找sayhello
類的實(shí)例方法诅福,最終都找不到,所以地址為0x0
拖叙。
- 首先獲取
- method3:
- 首先獲取
Person
的元類MetaClass(Person)
氓润,然后尋找沿著MetaClass→SuperMetaClass→RootMetaClass→NSObject→nil
去尋找Person
的元類繼承鏈的sayHappy
實(shí)例方法,通過(guò)前面的分析我們知道Person
的類方法sayHappy
就存在MetaClass(Person)
中薯鳍,所以返回打印的地址有值咖气。
- 首先獲取
- method4:
- 首先獲取
MetaClass(Person)
的元類,因?yàn)?code>MetaClass(Person)已經(jīng)是元類了挖滤,所以直接返回MetaClass(Person)
崩溪,然后尋找沿著MetaClass→SuperMetaClass→RootMetaClass→NSObject→nil
去尋方法名為sayHappy
的方法,通過(guò)前面的分析我們知道sayHappy
就存在MetaClass(Person)
中斩松,所以返回打印的地址有值伶唯。
- 首先獲取
class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name)
-
class_getMethodImplementation文檔
- 該方法會(huì)返回一個(gè)
指向方法實(shí)現(xiàn)的函數(shù)指針
- 速度比
method_getImplementation(class_getInstanceMethod(cls, name))
快 - 返回的
函數(shù)指針
不一定是該方法的實(shí)現(xiàn)
,也可能是runtime
的一個(gè)內(nèi)部函數(shù)
- 如果
類實(shí)例
無(wú)法響應(yīng)selector
惧盹,則返回的函數(shù)指針
將是runtime消息轉(zhuǎn)發(fā)機(jī)制
的一部分
- 該方法會(huì)返回一個(gè)
- 再來(lái)看看源碼
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;
}
通過(guò)源碼乳幸,首先去查找方法實(shí)現(xiàn),如果沒(méi)有找到钧椰,則進(jìn)行消息轉(zhuǎn)發(fā)粹断。
- imp1:
- 通過(guò)前面我們知道,
sayhello
實(shí)例方法嫡霞,存在Person類中瓶埋,能找到,所以返回函數(shù)指針诊沪,能打印出其地址
- 通過(guò)前面我們知道,
- imp2:
-
sayhello
不存在元類中养筒,所以進(jìn)行了消息轉(zhuǎn)發(fā)
-
- imp3:
-
sayHappy
是類方法,不存在類中娄徊,所以進(jìn)行了消息轉(zhuǎn)發(fā)
-
- imp4:
-
sayhappy
是類方法闽颇,存在元類中,能在元類中找到寄锐,所以返回函數(shù)指針兵多,能打印出其地址
-
iskindOfClass & isMemberOfClass
- 類方法
isKindOfClass
和isMemberOfClass
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[Person class] isKindOfClass:[Person class]];
BOOL re4 = [(id)[Person class] isMemberOfClass:[Person class]];
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
輸出如下
re1 :1
re2 :0
re3 :0
re4 :0
- 實(shí)例方法
isKindOfClass
和isMemberOfClass
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[Person alloc] isKindOfClass:[Person class]];
BOOL re8 = [(id)[Person alloc] isMemberOfClass:[Person class]];
輸出:
re5 :1
re6 :1
re7 :1
re8 :1
為什么會(huì)是這個(gè)結(jié)果,我們來(lái)分析一下
isKindOfClass
和isMemberOfClass
的源碼
- isKindOfClass
-
isKindOfClass
類方法源碼
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
通過(guò)源碼我們可以看到橄仆,當(dāng)
isKindOfClass
作為類方法的時(shí)候剩膘,是先通過(guò)ISA
獲取當(dāng)前類的元類,然后遞歸當(dāng)前的元類繼承鏈和當(dāng)前類進(jìn)行對(duì)比盆顾。
- re1:
-
NSObject
→ISA():RootMetaClass
VSNSObject
怠褐,不滿足,循環(huán)繼續(xù) -
根元類
的superclass
是NSObject
VSNSObject
您宪。返回YES
-
- re3
-
Peson
→ISA():MetaClass(Person)
VSPeson
奈懒,不滿足奠涌,循環(huán)繼續(xù) -
Meta(Person)
→superclass:SuperMetaClass(Peson)
VSPerson
, 不滿足,循環(huán)繼續(xù) -
SuperMeta(Peson)
→superclass:RootMetaClass
VSPerson
, 不滿足磷杏,循環(huán)繼續(xù) -
RootMetaClass
→superclass:NSObject
VSPerson
, 不滿足溜畅,循環(huán)繼續(xù) -
NSObject
→superclass:nil
,跳出循環(huán)极祸,返回NO
-
-
isKindOfClass
實(shí)例方法源碼
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
// class的實(shí)例方法
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
objc_object::getIsa()
{
if (fastpath(!isTaggedPointer())) return ISA();
extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
uintptr_t slot, ptr = (uintptr_t)this;
Class cls;
slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
cls = objc_tag_classes[slot];
if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
cls = objc_tag_ext_classes[slot];
}
return cls;
}
通過(guò)源碼慈格,當(dāng)
isKindOfClass
作為實(shí)例方法的時(shí)候,先獲取當(dāng)前實(shí)例對(duì)象的isa
即當(dāng)前實(shí)例的類遥金,與傳入類進(jìn)行對(duì)比浴捆,然后再把傳入類與當(dāng)前實(shí)例對(duì)象的類的繼承鏈
來(lái)進(jìn)行對(duì)比
- re5:
-
[NSObject alloc]
→getIsa() :NSObject
VSNSObject
,返回YES
-
- re7:
-
[Person alloc]
→getIsa() :Person VS
VSPerson
稿械,返回YES选泻。
-
!C滥滔金!注意這里有坑點(diǎn)
看上去分析的很對(duì),實(shí)際上是這樣的么茂嗓?我們進(jìn)行斷點(diǎn)調(diào)試的時(shí)候發(fā)現(xiàn)isKindOfClass不走我們上面分析的兩個(gè)方法,而是統(tǒng)一走objc_opt_isKindOfClass
??接著我們來(lái)分析
objc_opt_isKindOfClass
的源碼
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
Class cls = obj->getIsa();
if (fastpath(!cls->hasCustomCore())) {
for (Class tcls = cls; tcls; tcls = tcls->superclass) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
看到源碼科阎,是不是和上面分析的是基本一致的述吸,首先通過(guò)需要判斷的對(duì)象或者類的isa來(lái)獲取當(dāng)對(duì)象的類或者類的元類,然后通過(guò)他們的繼承鏈來(lái)與需要對(duì)比的類進(jìn)行對(duì)比
Tips:類通過(guò)isa可以獲取元類锣笨,對(duì)象通過(guò)isa獲取實(shí)例當(dāng)前對(duì)象的類
-
isMemberOfClass
為了避免剛剛的問(wèn)題蝌矛,我們首先來(lái)看下isMemberOfClass方法的調(diào)用走哪個(gè)方法
確實(shí)會(huì)走isMemberOfClass的底層方法,可以放心分析了 -
isMemberOfClass
類方法的源碼
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
通過(guò)源碼错英,拿元類和傳入類來(lái)進(jìn)行對(duì)比
- r2:
-
NSObject→ISA()
:RootMetaClass
VSNSObject
入撒,返回NO
-
- r4:
-
Person→ISA()
:MetaClass(Person)
VSNSObject
,返回NO
-
-
isMemberOfClass
實(shí)例方法的源碼
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
// class的實(shí)例方法
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
objc_object::getIsa()
{
if (fastpath(!isTaggedPointer())) return ISA();
extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
uintptr_t slot, ptr = (uintptr_t)this;
Class cls;
slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
cls = objc_tag_classes[slot];
if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
cls = objc_tag_ext_classes[slot];
}
return cls;
}
分析源碼椭岩,
[self class]
其實(shí)就是通過(guò)isa
去獲取實(shí)例化當(dāng)前對(duì)象的類茅逮,然后與傳入類來(lái)進(jìn)行對(duì)比。
- r6:
-
[NSObject alloc]→getIsa()
:NSObject
VSNSObject
判哥,返回YES
-
- r4:
-
[Person alloc]→getIsa()
:Person
VSPerson
献雅,返回YES
-