屬性 & 成員變量 & 實(shí)例變量
屬性(property)有setter馁筐、getter方法涂召;
成員變量(ivar)沒(méi)有setter、getter方法敏沉;屬性 = 下劃線成員變量 + setter + getter方法
實(shí)例變量:特殊的成員變量--是類的實(shí)例化
// 成員變量 vs 屬性
@interface Book : NSObject
{
NSString * name; // 成員變量果正,諸如:NSArray,NSDictionary...
NSObject *objc; // 實(shí)例變量-特殊的成員變量 類的實(shí)例化
UIButton * btn; // 實(shí)例變量盟迟,諸如:UIView秋泳、UILabel、UITextView...
}
元類 & 元類方法
上一章OC底層原理04中队萤,在類中轮锥,屬性、實(shí)例方法都在bits
中找到要尔。對(duì)象方法不在對(duì)象中舍杜,而在類中;類方法卻存在其元類中赵辕。
為了驗(yàn)證既绩,繼續(xù)在781源碼中通過(guò)LLDB調(diào)試
打印之后,結(jié)果如下:
元類中為什么會(huì)有類方法还惠?
首先定義兩個(gè)類Person饲握,Teacher
#import <Foundation/Foundation.h>
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
#import "LGPerson.h"
@implementation LGPerson
- (void)sayHello{
NSLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"LGPerson say : Happy!!!");
}
@end
在main中首先定義可以獲取類中方法名的方法
void lgObjc_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));
LGLog(@"Method, name: %@", key);
}
free(methods);
}
然后調(diào)用并打印
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
lgObjc_copyMethodList(pClass);
其結(jié)果打印為:
Method, name: sayHello // 只有一個(gè)對(duì)象方法,能打印出sayHello
元類中未什么會(huì)出現(xiàn)類方法這是有會(huì)可能出現(xiàn)在面試題中的,當(dāng)然方法不會(huì)這么直接的問(wèn)救欧。
- 定義一個(gè)方法
lgInstanceMethod_classToMetaclass
衰粹,獲取類的實(shí)例方法
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);
}
打印結(jié)果
lgInstanceMethod_classToMetaclass - 0x1000031b0-0x0-0x0-0x100003148
class_getInstanceMethod 返回的是類的實(shí)例方法,如果在傳入的類或者類的父類中沒(méi)有找到指定的實(shí)例方法笆怠,則返回空
method1铝耻、method4都打印出地址,method2蹬刷、method3為空瓢捉,進(jìn)一步印證:對(duì)象方法存在類中,而類方法存在元類中
- 定義一個(gè)
lgClassMethod_classToMetaclass
方法,用于獲取類的類方法
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));
// 元類 為什么有 sayHappy 類方法
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
打印結(jié)果
lgClassMethod_classToMetaclass-0x0-0x0-0x100003148-0x100003148
method1办成、method2中未找到泡态,method3、method4找到了類方法迂卢。首先我們?cè)賮?lái)看看class_getClassMethod
某弦,它是用來(lái)獲取類
/***********************************************************************
* class_getClassMethod. Return the class method for the specified
* class and selector.
**********************************************************************/
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
獲取類的類方法就是等同于獲取元類的實(shí)例方法,最后走的依然是class_getInstanceMethod
而克。不同的是cls->getMeta():
獲得元類刀崖,而在此例中,從元類中獲取元類拍摇,也就是根元類,但是它直接返回了自身馆截。
在原理04中分析到isa走位圖充活,類的元類是與類同名的元類,元類的元類是superMeta(如果有)蜡娶,superMeta的元類是根元類混卵,根元類的元類是根元類自身,如果再繼續(xù)查找就會(huì)陷入遞歸循環(huán)窖张,一直查找的是根元類幕随。
所以蘋果在判斷獲取元類的類方法時(shí),判斷如果cls是元類宿接,就返回元類自身this赘淮;否則就返回類cls的isa,即cls的元類睦霎,這也就是method3能找到sayHappy類方法的解釋梢卸。
所以類中存在類方法,去元類尋找類方法時(shí)副女,就返回元類自身蛤高,自然也就能在元類中找到類方法了。
// NOT identical to this->ISA when this is a metaclass
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
- 定義一個(gè)
lgIMP_classToMetaclass
方法,用于找尋是否存在方法實(shí)現(xiàn)
void lgIMP_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__);
}
SEL
: 類成員方法的指針戴陡,它與C語(yǔ)言中的函數(shù)指針有所不同塞绿,函數(shù)指針是保存了方法的地址,但SEL
只是獲取方法編號(hào)恤批。@selector()
就是取類方法的編號(hào)异吻。
IMP
: 函數(shù)指針,保存著方法的地址。
class_getMethodImplementation
:返回具體方法的實(shí)現(xiàn)开皿。Developer Documentation 瞄一下:
大概說(shuō):向這個(gè)函數(shù)中傳遞一個(gè)類的實(shí)例消息時(shí)涧黄,它將返回傳入方法實(shí)現(xiàn)函數(shù)的指針。順帶說(shuō)了下赋荆,
class_getMethodImplementation
比method_getImplementation(class_getInstanceMethod(cls, name))
更快笋妥。而返回的函數(shù)指針可能是runtime內(nèi)部的函數(shù),而不一定是實(shí)際的方法實(shí)現(xiàn)窄潭。 如果類的實(shí)例不響應(yīng)傳入方法春宣,則返回的函數(shù)指針將成為runtime消息轉(zhuǎn)發(fā)機(jī)制的一部分。
打印結(jié)果
0x100001d00-0x7fff6b58c580-0x7fff6b58c580-0x100001d30
imp1:實(shí)例方法sayHello
在pClass
中有具體實(shí)現(xiàn)嫉你,所以返回該方法imp函數(shù)指針地址月帝。
imp2:實(shí)例方法sayHello
存在類中,并未在metaClass
中幽污,所以進(jìn)行消息轉(zhuǎn)發(fā)嚷辅。
imp3:類方法sayHappy
存在元類中,并未在pClass
中自然不會(huì)有具體實(shí)現(xiàn)距误,所以進(jìn)行消息轉(zhuǎn)發(fā)簸搞。
imp4:類方法sayHappy
存在元類中,且有具體實(shí)現(xiàn)准潭,所以返回的imp函數(shù)指針地址趁俊。
總結(jié)
- 對(duì)象方法不在對(duì)象中,而在類中刑然;類方法卻存在其元類中寺擂。
- 獲取類的類方法就是等同于獲取元類的實(shí)例方法,元類中會(huì)有類方法
- 獲取元類的類方法時(shí)泼掠,如果cls是元類怔软,就返回元類自身this;否則就返回類cls的isa择镇,即cls的元類爽雄。
-
class_getInstanceMethod
:返回的是類的實(shí)例方法,如果在傳入的類或者類的父類中沒(méi)有找到指定的實(shí)例方法沐鼠,則返回空挚瘟。 -
class_getClassMethod
:獲取類方法叹谁,如果傳入的類或者類的父類中不包含傳入的類方法,則為空乘盖。 -
class_getMethodImplementation
:返回具體方法的實(shí)現(xiàn),如果未找到焰檩,則進(jìn)行消息轉(zhuǎn)發(fā)。
拓展面試題:iskindOfClass & isMemberOfClass 差別
- 類方法調(diào)用
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(@"\n re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
打印結(jié)果:
re1 :1
re2 :0
re3 :0
re4 :0
先來(lái)瞅一瞅源碼
+ (BOOL)isKindOfClass:(Class)cls {
// 獲取類的元類 vs 傳入類
// 根元類 vs 傳入類
// 根類 vs 傳入類
// 如:re3 元類->根元類->NSObject -> nil vs LGPerson ,return NO
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
//獲取類的元類订框,與 cls對(duì)比
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
// objc_object.h,之前分析部分
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
}
+ isKindOfClass
第一次是將獲取cls的metalClass 與 cls對(duì)比析苫,再對(duì)比的是獲取上次結(jié)果的父類superClass與 cls 進(jìn)行對(duì)比 ,到根元類的父類與cls 對(duì)比穿扳,如果一直沒(méi)有衩侥,一直追溯到根類的父類nil 與 cls對(duì)比
此例中對(duì)比過(guò)程:元類(isa) -> 根元類(父類) -> 根類NSObject(父類) -> nil(父類) 與 cls的對(duì)比
。
+isMemberOfClass
將獲取類的元類與 cls對(duì)比矛物,有則返回YES茫死。
re1 :1 // NSObject vs NSObject(傳入類) -> YES
re1 :0 // NSObjec -> 根元類 vs NSObject(傳入類) ->NO
re1 :0 // LGPerson -> 元類(首次是找元類) -> 根元類(開(kāi)始找父類) -> 根類(NSObject)-> nil vs cls(LGPseron),從LGPerson依次查找元類履羞,到后續(xù)查找父類查找對(duì)比都不相等峦萎,輸出 NO
re1 :0 // LGPerson的元類 vs cls(LGPerson),-> NO
- 實(shí)例方法調(diào)用
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(@" \nre5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
打印結(jié)果:
re5 :1
re6 :1
re7 :1
re8 :1
再看看源碼:
// 將獲取的對(duì)象類 與 傳入類cls對(duì)比忆首,相等返回YES爱榔,停止循環(huán)。如果不相等糙及,將上次獲取的 類的父類 與傳入類cls 對(duì)比详幽,依然循環(huán)對(duì)比
- (BOOL)isKindOfClass:(Class)cls {
// 獲取對(duì)象的類 vs 傳入的類
// 父類 vs 傳入的類
// 根類 vs 傳入的類
// nil vs 傳入的類
// 如:LGPerson -> NSObject -> nil vs LGPerson
*/
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
// 獲取對(duì)象的類,與 傳入類對(duì)比
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
- isKindOfClass
將 將獲取的對(duì)象類 與 傳入類cls對(duì)比浸锨,相等返回YES妒潭,停止循環(huán)。如果不相等揣钦,將上次獲取的 類的父類 與傳入類cls 對(duì)比,依然循環(huán)對(duì)比
此例中對(duì)比過(guò)程:類對(duì)象 -> 父類(如果有) -> 根類NSObject -> nil 與 cls的對(duì)比
漠酿。
-isMemberOfClass
將獲取對(duì)象的類冯凹,與 傳入類cls對(duì)比,有則返回YES,否則返回NO炒嘲。
re5 :1 // 對(duì)象的isa=NSObject(根類) vs NSObject(傳入類宇姚,即根類) -> YES
re6 :1 // 對(duì)象的類=NSObject(根類) vs NSObject(傳入類,即根類) -> YES
re7 :1 // 對(duì)象的類=LGPseron vs cls(LGPseron)夫凸,為YES浑劳,無(wú)需查找父類
re8 :1 // 對(duì)象的類=LGPerson vs cls(LGPerson),為YES