oc中方法存在于類中未玻,類方法存在于元類中
class_getInstanceMethod
//cls 類
Method class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
意思是這個(gè)
class_getInstanceMethod
會(huì)返回類的實(shí)例方法
問題1
LGPerson類
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation LGPerson
- (void)sayHello{
NSLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"LGPerson say : Happy!!!");
}
@end
main.m
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);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
lgInstanceMethod_classToMetaclass(pClass);
NSLog(@"Hello, World!");
}
return 0;
}
問:輸出情況?
-
sayHello
是實(shí)例方法胡控,sayHappy
是類方法扳剿。 -
pClass
是類,metaClass
是元類
根據(jù)原則oc中實(shí)例方法存在與類中昼激,類方法存在與元類中
大致可以推斷出:
sayHello
是實(shí)例方法在LGPerson類中舞终,所以
- method1:打印出sayHello的內(nèi)存地址,
- method2:NULL
sayHappy
是累方法癣猾,在LGPerson的元類中 - method3:輸出是0x00即NULL
- method4:輸出是sayHappy的內(nèi)存地址
class_getClassMethod
問題2
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);
}
問:輸出情況敛劝?
-
sayHello
是實(shí)例方法,sayHappy
是類方法纷宇。 -
pClass
是類夸盟,metaClass
是元類
根據(jù)原則oc中實(shí)例方法存在與類中,類方法存在與元類中
輸出情況
- method1:
0x00
即nil - method2:
0x00
即nil - method3:
0x100008148
- method4:
0x100008148
sayHello是實(shí)例方法像捶,所以class_getClassMethod
不論怎么查詢都是nil
sayHappy是類方法上陕,類方法存在于元類中,但是method3和method4都打印了地址拓春,释簿, 這是為什么呢?
可以查看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();
}
//獲取isa
inline Class
objc_object::ISA()
{
ASSERT(!isTaggedPointer());
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
return (Class)(isa.bits & ISA_MASK);
#endif
}
通過查看源碼發(fā)現(xiàn)
class_getClassMethod
本質(zhì)還是調(diào)用class_getInstanceMethod
庶溶,在傳入的第一個(gè)參數(shù)的時(shí)候傳入的是Class的元類cls->getMeta()
。
所以method3自然就能打印出來
在getMeta()會(huì)判斷當(dāng)前的class是否為元類懂鸵,如果是就返回本身偏螺。
所以method4自身也能打印出來。
isKindOfClass和isMemberOfClass
isKindOfClass分別有類方法和實(shí)例方法
- isKindOfClass:是給定類的實(shí)例還是從該類繼承的任何類的實(shí)例
- isMemberOfClass:是否給定類的實(shí)例
問:
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson 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);
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson 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);
+isKindOfClass 和 - isKindOfClass的區(qū)別匆光,源碼
//類方法
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
通過源碼結(jié)構(gòu)isa的走位圖
類方法是獲取當(dāng)前類的元類套像,不斷獲取元類父類,與傳入的類型進(jìn)行比較
實(shí)例方法是獲取當(dāng)前的類终息,不斷獲取父類夺巩,與傳入的類型進(jìn)行比較。
實(shí)際代碼運(yùn)行并沒有調(diào)用上述代碼周崭,而是通過llvm優(yōu)化后調(diào)用如下方法:
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
//obj如果是實(shí)例對(duì)象柳譬, obj->getIsa()獲取的是類信息
//obj如果是類對(duì)象, obj->getIsa()獲取的是元類信息
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);
}
id:typedef struct objc_object *id;
id是任意類型的objc_object
Class:class是objc_class類型休傍,而objc_class是繼承objc_object的征绎。
struct objc_class : objc_object {}
typedef struct objc_class *Class;
所以objc_opt_isKindOfClass的第一個(gè)參數(shù)obj
- obj如果是實(shí)例對(duì)象, obj->getIsa()獲取的是類class
- obj如果是類對(duì)象, obj->getIsa()獲取的是元類MetaClass
解析1:
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
objc_opt_isKindOfClass的源碼分析
傳入的參數(shù)是[NSObject class]人柿,通過obj->getIsa()
獲取到是NSObject的根元類柴墩,所以在第一次for循環(huán)的時(shí)候,cls是根元類與otherClass根類想比較凫岖,則第一次比較不相等江咳。
第二次for循環(huán)cls獲取其父類,根據(jù)isa的走位哥放,根元類的父類是NSObject類歼指,所以第二次循環(huán)相等。
Class cls = obj->getIsa();//根元類甥雕,根元類的父類是NSObject
if (fastpath(!cls->hasCustomCore())) {
for (Class tcls = cls; tcls; tcls = tcls->superclass) {
if (tcls == otherClass) return YES;
}
return NO;
}
所以輸出YES
解析2
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
isMemberOfClass是判斷一個(gè)對(duì)象是否給定類的實(shí)例
所以返回NO
解析3
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
objc_opt_isKindOfClass在根據(jù)其源碼和isa走位圖踩身。
傳入的obj是LGPerson的類class,經(jīng)過obj->getIsa()獲取LGPerson元類metaclass社露,而otherclass是LGPerson的類class挟阻,metaclass與class比較所以第一次循環(huán)不相等。metaclass獲取其父類峭弟,因此與LGPerson的類class相比不相等附鸽。所以不斷循環(huán)后不相等
所以返回NO
解析4
BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];
返回NO
解析5-7
objc_opt_isKindOfClass傳入的第一個(gè)參數(shù)是個(gè)對(duì)象,經(jīng)過objc->getIsa()獲取的是類class瞒瘸,class與otherclass相比較是相等坷备。
返回yes
解析6-8
返回yes
2020-09-30 17:50:06.470167+0800 KCObjc[80323:9507443] re1 :1
re2 :0
re3 :0
re4 :0
2020-09-30 17:50:06.471930+0800 KCObjc[80323:9507443] re5 :1
re6 :1
re7 :1
re8 :1