這篇我們接著分析類。在iOS開發(fā)中我們經(jīng)常會(huì)使用到實(shí)例方法和類方法。那實(shí)例方法和對(duì)象方法是存放在哪里的呢?今天,我們就來一探究竟。
一、屬性、成員變量、實(shí)例變量
同樣使用LPPerson作為演示,分別給LPPerson添加兩個(gè)屬性和實(shí)例變量,再利用runtime的API獲取LPPerson的成員變量和屬性并打印。
void LogObjc_copyIvar_copyProperies(Class pClass){
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Ivar const ivar = ivars[i];
//獲取實(shí)例變量名
const char*cName = ivar_getName(ivar);
NSString *ivarName = [NSString stringWithUTF8String:cName];
NSLog(@"class_copyIvarList:%@",ivarName);
}
free(ivars);
unsigned int pCount = 0;
objc_property_t *properties = class_copyPropertyList(pClass, &pCount);
for (unsigned int i=0; i < pCount; i++) {
objc_property_t const property = properties[i];
//獲取屬性名
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
//獲取屬性值
NSLog(@"class_copyProperiesList:%@",propertyName);
}
free(properties);
}
@interface LPPerson : NSObject
{
NSString *hobby;
NSObject *objc;
}
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, strong) NSString *name;
@end
@implementation LPPerson
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
LPPerson *person = [LPPerson alloc];
Class pClass = object_getClass(person);
LogObjc_copyIvar_copyProperies(pClass);
NSLog(@"Hello, World!");
}
return 0;
}
我們運(yùn)行,看一下打印結(jié)果:
class_copyIvarList:hobby
class_copyIvarList:objc
class_copyIvarList:_nickName
class_copyIvarList:_name
class_copyProperiesList:nickName
class_copyProperiesList:name
通過結(jié)果,我們可以發(fā)現(xiàn):
- 結(jié)合以前的知識(shí),我們可以知道,屬性 = 成員變量 +
setter+getter - 屬性的成員變量是帶下劃線_的,
{}中的成員變量是不帶_的
那什么叫實(shí)例變量呢:
實(shí)例變量本質(zhì)上就是成員變量,只是實(shí)例是針對(duì)類而言,實(shí)例是指類的聲明。{ }中的objc就是實(shí)例變量。NSObject 是OC類,所以objc屬于實(shí)例變量。但是并不是所有的成員變量都是實(shí)例變量,OC常用基本數(shù)據(jù)類型申明的成員變量和NSString類型不是成員變量。
NSString雖然繼承于NSObject,但是仍然是常量類型, 因?yàn)椴荒芴砑訉傩?,如果定義在類中的{}中,是成員變量
也可以理解為:實(shí)例變量 + 基本數(shù)據(jù)類型 + NSString = 成員變量
二、方法的歸屬
繼續(xù)使用LPPerson,在它的.h中新增實(shí)例方法sayHello和類方法sayHappy,并在.m中實(shí)現(xiàn)
@interface LPPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation LPPerson
- (void)sayHello{
NSLog(@"LPPerson say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"LPPerson say : Happy!!!");
}
@end
然后在main.m中實(shí)現(xiàn)如下代碼:
void LogObjc_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);
}
void LpInstanceMethod_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 LpClassMethod_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));
NSLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
void LpIMP_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(sayHappy));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
NSLog(@"%s",__func__);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
LPPerson *person = [LPPerson alloc];
Class pClass = object_getClass(person);
LogObjc_copyMethodList(pClass);
LpInstanceMethod_classToMetaclass(pClass);
LpClassMethod_classToMetaclass(pClass);
LpIMP_classToMetaclass(pClass)
NSLog(@"Hello, World!");
}
return 0;
}
看下運(yùn)行結(jié)果:
Method, name: sayHello
lgInstanceMethod_classToMetaclass:------ 0x1000031b0---0x0---0x0---0x100003148
lgClassMethod_classToMetaclass:------ 0x0---0x0---0x100003148---0x100003148
lgIMP_classToMetaclass:------ 0x100001d10---0x7fff6f198580---0x7fff6f198580---0x100001d40
由上篇中isa和superClass走位圖我們可以知道:
- 實(shí)例對(duì)象和其父類的關(guān)系是:
類->父類->根類->nil - 元類和其父類的關(guān)系是:
元類->根元類->根類->nil
方法查找時(shí)也是按照上面的順序進(jìn)行查找,如果找到就返回,沒有找到則返回NULL
我們根據(jù)打印結(jié)果來分析下:
LogObjc_copyMethodList
去獲取LPPerson的方法列表發(fā)現(xiàn),只有一個(gè)實(shí)例方法:sayHello
實(shí)例方法是存在類中,所以此時(shí)只能找實(shí)例方法sayHello。
LpInstanceMethod_classToMetaclass
class_getInstanceMethod是用來在類中獲取實(shí)例方法,如果在傳入的類或者類的父類中沒有找到指定的實(shí)例方法,則返回NULL
- 第一個(gè)參數(shù)表示在哪個(gè)類中獲取方法
- 第二個(gè)是需要獲取的方法
我們?cè)賮矸治鱿?code>LpInstanceMethod_classToMetaclass:
在LPPerson和LPPerson的元類中,分別獲取sayHello和sayHappy的實(shí)例方法
-method1:在LPPerson中獲取sayHello實(shí)例方法,LPPerson類中有,所以可以獲取
-
method2:在LPPerson的元類中獲取sayHello實(shí)例方法,LPPerson的元類中沒有,根元類沒有,根類沒有,所以返回NULL,地址就為0x0 -
method3:在LPPerson的獲取sayHappy實(shí)例方法,LPPerson中沒有,LPPerson的父類NSObsject沒有,所以返回NULL -
method4:在LPPerson元類中的獲取sayHappy實(shí)例方法,LPPerson中沒有,所以去LPPerson的元類中找,而類方法就是元類的是實(shí)例方法,所以LPPerson的元類獲取到了。
LpClassMethod_classToMetaclass
class_getClassMethod:是用來類中獲取類方法,如果在傳入的類或者類的父類中沒有找到指定的實(shí)例方法,則返回NULL
我們?cè)倏聪?code>class_getClassMethod源碼:
//獲取類方法
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 判斷是否是元類,是元類就直接返回,反之,繼續(xù)找isa指向
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
可以得出class_getClassMethod的實(shí)現(xiàn)是獲取類的類方法,其本質(zhì)就是獲取元類的實(shí)例方法,最終還是會(huì)走到class_getInstanceMethod,但是在這里需要注意的一點(diǎn)是:在getMeta源碼中,如果判斷出cls是元類,那么就不會(huì)再繼續(xù)往下遞歸查找,會(huì)直接返回this,其目的是為了防止元類的無限遞歸查找
再來分析下LpClassMethod_classToMetaclass:
在LPPerson和LPPerson的元類中,分別獲取sayHello和sayHappy的類方法
-
method1:在LPPerson中獲取sayHello類方法,LPPerson中沒有,LPPerson的父類NSObsject也沒有,nil也沒有所以返回NULL -
method2:在LPPerson的元類中獲取sayHello類方法,LPPerson的元類中沒有,根元類沒有,根類沒有,所以返回NULL,地址就為0x0 -
method3:在LPPerson的獲取sayHappy類方法,LPPerson中沒有,但是LPPerson元類中找,LPPerson元類存儲(chǔ)的實(shí)例方法就是LPPerson類方法,所以可以找到 -
method4:在LPPerson元類中的獲取sayHappy類方法,當(dāng)前傳入的是LPPerson元類,所以開始在LPPerson元類中去查找實(shí)例方法,即class_getInstanceMethod(cls->getMeta(), sel),而LPPerson的類方法,就是LPPerson元類存儲(chǔ)的實(shí)例方法,所以可以獲取到。
lpIMP_classToMetaclass
class_getMethodImplementation:
其大致含義就是:該函數(shù)在向類實(shí)例發(fā)送消息時(shí)會(huì)被調(diào)用,并返回一個(gè)指向方法實(shí)現(xiàn)函數(shù)的指針。這個(gè)函數(shù)會(huì)比method_getImplementation(class_getInstanceMethod(cls, name))更快。返回的函數(shù)指針可能是一個(gè)指向runtime內(nèi)部的函數(shù),而不一定是方法的實(shí)際實(shí)現(xiàn)。如果類實(shí)例無法響應(yīng)selector,則返回的函數(shù)指針將是運(yùn)行時(shí)消息轉(zhuǎn)發(fā)機(jī)制的一部分
再次查看源碼:
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
//查找方法實(shí)現(xiàn)
imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
//如果沒有找到,則進(jìn)行消息轉(zhuǎn)發(fā)
if (!imp) {
return _objc_msgForward;
}
return imp;
}
-
imp1:在LPPerson中查找sayHello實(shí)現(xiàn),因?yàn)?code>sayHello是實(shí)例方法,存在類中,所以可以找到返回對(duì)應(yīng)的實(shí)現(xiàn) -
imp2:在LPPerson元類中查找sayHello實(shí)現(xiàn),因?yàn)?code>LPPerson元類沒有,所以進(jìn)行了消息轉(zhuǎn)發(fā) -
imp3:在LPPerson類中查找sayHappy實(shí)現(xiàn),因?yàn)?code>sayHappy是類方法,類方法是存在元類中的,LPPerson類沒有,所以進(jìn)行了消息轉(zhuǎn)發(fā) -
imp4:在LPPerson元類中查找sayHappy實(shí)現(xiàn),因?yàn)?code>sayHappy是類方法,類方法是存在元類中的,LPPerson元類中,所以可以找到返回對(duì)應(yīng)的實(shí)現(xiàn)
三、經(jīng)典題目分析:
//-----使用 iskindOfClass & isMemberOfClass 類方法
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);
//------iskindOfClass & isMemberOfClass 實(shí)例方法
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);
上面打印結(jié)果是什么?各位可以先思考一下,再看下面的結(jié)果
結(jié)果如下:
re1 :1
re2 :0
re3 :0
re4 :0
re5 :1
re6 :1
re7 :1
re8 :1
在分析結(jié)果之前,我們先看一下isKindOfClass和isMemberOfClass的源碼:
isKindOfClass源碼,分為類方法和實(shí)例方法:
//--isKindOfClass---類方法、對(duì)象方法
//+ isKindOfClass:第一次比較是 獲取類的元類 與 傳入類對(duì)比,再次之后的對(duì)比是獲取上次結(jié)果的父類 與 傳入 類進(jìn)行對(duì)比
+ (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:第一次是獲取對(duì)象類 與 傳入類對(duì)比,如果不相等,后續(xù)對(duì)比是繼續(xù)獲取上次 類的父類 與傳入類進(jìn)行對(duì)比
- (BOOL)isKindOfClass:(Class)cls {
/*
獲取對(duì)象的類 vs 傳入的類
父類 vs 傳入的類
根類 vs 傳入的類
nil vs 傳入的類
*/
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
isMemberOfClass源碼,同樣分為類方法和實(shí)例方法
//-----類方法
//+ isMemberOfClass : 獲取類的元類,與 傳入類對(duì)比
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
//-----實(shí)例方法
//- isMemberOfClass : 獲取對(duì)象的類,與 傳入類對(duì)比
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
根據(jù)源碼,我們可以總結(jié)如下:
-
isKindOfClass
類方法:元類 --> 根元類 --> 根類 -->nil與 傳入類的對(duì)比
實(shí)例方法:對(duì)象的類 --> 父類 --> 根類 -->nil與 傳入類的對(duì)比 -
isMemberOfClass
類方法: 類的元類 與 傳入類 對(duì)比
實(shí)例方法:對(duì)象的父類 與 傳入類 對(duì)比
接下來,我們?cè)賮矸治錾厦骖}目:
re1:
[NSObject class]執(zhí)行的是isKindOfClass類方法,所以:
NSObject的元類是根元類,根元類!=NSObject,
繼續(xù)尋找根元類的父類,即根類,根類即為NSObject。
NSObject==NSObject所以為1。re2:
[NSObject class]執(zhí)行的是isMemberOfClass類方法,所以:
NSObject的元類是根元類,根元類!=NSObject,所以為0re3:
[LGPerson class]執(zhí)行的是isKindOfClass類方法,所以:
LGPerson的元類!=LGPerson,
繼續(xù)LGPerson的元類的父類是根元類,根元類 !=LGPerson,
繼續(xù)根元類的父類即根類,根類!=LGPerson
根類的父類是nil,nil != LGPerson,
所以為0re4:
[LGPerson class]執(zhí)行的是isMemberOfClass類方法,所以:
LGPerson的元類 !=LGPerson,所以為0
re5:
[NSObject alloc]執(zhí)行的是isKindOfClass實(shí)例方法,所以:
[NSObject alloc]的類NSObject,NSObject==NSObject,所以為1。re6:
[NSObject alloc]執(zhí)行的是isMemberOfClass實(shí)例方法,所以:
[NSObject alloc]的類NSObject,NSObject==NSObject,所以為1re7:
[LGPerson alloc]執(zhí)行的是isKindOfClass實(shí)例方法,所以:
[LGPerson alloc]的類是LGPerson,LGPerson== LGPerson,所以為1re8:
[LGPerson alloc]執(zhí)行的是isMemberOfClass實(shí)例方法,所以:
[LGPerson alloc]的類是LGPerson,LGPerson == LGPerson,所以為1
覺得不錯(cuò)記得點(diǎn)贊哦!聽說看完點(diǎn)贊的人逢考必過,逢獎(jiǎng)必中。?( ′???` )比心