做企業(yè)網(wǎng)站用哪個(gè)軟件產(chǎn)品推廣平臺(tái)排行榜
- isa指針:isa指針是一個(gè)指向?qū)ο笏鶎兕惢蛟惖闹羔?。它決定了對(duì)象可以調(diào)用的方法和屬性。isa指針在對(duì)象的結(jié)構(gòu)中存在,并且在運(yùn)行時(shí)會(huì)被自動(dòng)設(shè)置。
- isa 指針,表示這個(gè)對(duì)象是一個(gè)什么類。而 Class 類型, 也就是 struct objc_class * ,這是蘋果在下面的注釋中寫到的。這說(shuō)明類本身也是一個(gè)對(duì)象。在類對(duì)象中的 isa 指向的類叫做“元類”,類方法就定義在元類中。總的來(lái)說(shuō)就是,一個(gè)類可以有很多的實(shí)例,這些實(shí)例有著唯一的一個(gè)類對(duì)象,而這個(gè)類對(duì)象也有著唯一的一個(gè)元類。
在Objective-C中,每個(gè)對(duì)象都有一個(gè)isa指針,它指向該對(duì)象所屬的類或元類。isa指針決定了對(duì)象可以調(diào)用的方法和屬性。通過isa指針,Objective-C運(yùn)行時(shí)可以在運(yùn)行時(shí)動(dòng)態(tài)地確定對(duì)象所屬的類,并在該類或其父類中查找對(duì)應(yīng)的方法實(shí)現(xiàn)。
下面是一些示例代碼來(lái)說(shuō)明isa指針的作用:
@interface MyClass : NSObject
- (void)myMethod;
@end@implementation MyClass
- (void)myMethod {NSLog(@"MyClass's myMethod");
}
@endint main() {MyClass *myObject = [[MyClass alloc] init];[myObject myMethod];return 0;
}
在上面的示例中,創(chuàng)建了一個(gè)名為MyClass
的類,它繼承自NSObject
。MyClass
類中定義了一個(gè)名為myMethod
的方法。
當(dāng)我們創(chuàng)建一個(gè)MyClass
對(duì)象并調(diào)用myMethod
方法時(shí),實(shí)際上發(fā)生了以下過程:
- 分配內(nèi)存并初始化
MyClass
對(duì)象。 - 運(yùn)行時(shí)為該對(duì)象設(shè)置isa指針,使其指向
MyClass
的類對(duì)象。 - 在
myObject
上調(diào)用myMethod
方法時(shí),運(yùn)行時(shí)首先通過isa指針找到MyClass
的類對(duì)象。 - 運(yùn)行時(shí)在類對(duì)象中查找名為
myMethod
的方法實(shí)現(xiàn)并執(zhí)行。
通過這個(gè)過程,我們可以看到isa指針在動(dòng)態(tài)確定對(duì)象所屬的類的過程中起到了關(guān)鍵作用。它使得我們可以在運(yùn)行時(shí)根據(jù)對(duì)象的實(shí)際類型來(lái)調(diào)用適當(dāng)?shù)姆椒ā?/p>
isa,類對(duì)象,元類對(duì)象
OC的對(duì)象及其alloc和init。驗(yàn)證了OC對(duì)象底層是結(jié)構(gòu)體,然后在alloc的方法調(diào)用棧的最后一個(gè)關(guān)鍵方法creatinstance中,有一個(gè)創(chuàng)建isa指針的方法。這篇,我們就先聊一聊isa指針。
我們知道,OC中的絕大部分對(duì)象,都是繼承自NSObject(目前我已知的只有一個(gè) NSProxy 類不繼承自NSObject,它跟NSObject一樣,是基類)。按住command鍵,跳進(jìn)NSObject的頭文件,就能看到下面的代碼:
@interface NSObject <NSObject> {#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wobjc-interface-ivars"Class isa OBJC_ISA_AVAILABILITY;#pragma clang diagnostic pop
}
這就是NSObject的聲明了,它只有一個(gè)isa指針,萬(wàn)物繼承自NSObject,就有了自己的isa指針了。那這個(gè)isa到底有什么用呢?它究竟是個(gè)啥東西?
位域
在說(shuō)isa之前,我們先了解一下[[共用體]]和[[位域]]這種技術(shù)。
在armv7、armv7s時(shí)代,由于是32位操作系統(tǒng),蘋果并沒有對(duì)指針進(jìn)行優(yōu)化,實(shí)例對(duì)象的isa是直接指向類對(duì)象的。進(jìn)入到arm64架構(gòu)時(shí)代后isa占用8個(gè)字節(jié),共計(jì)64位,如果只是單純存一個(gè)指針太浪費(fèi),所以,蘋果通過共用體技術(shù),充分的讓這64位存儲(chǔ)了非常多的信息,不用再額外的開辟空間來(lái)存儲(chǔ),減少了內(nèi)存開支,減少了內(nèi)存的操作。下面我們具體看一下isa的結(jié)構(gòu)。
isa的定義
union isa_t {isa_t() { }isa_t(uintptr_t value) : bits(value) { }Class cls;uintptr_t bits;
#if defined(ISA_BITFIELD)struct{ISA_BITFIELD; // defined in isa.h 說(shuō)明在isa.h中};
#endif
};
里面有個(gè)ISA_BITFIELD,再看這個(gè)是怎么定義的:
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \uintptr_t nonpointer : 1; //表明這個(gè)屬性占一位,且從低位開始\uintptr_t has_assoc : 1; // 同上 \uintptr_t has_cxx_dtor : 1; \uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \uintptr_t magic : 6; \uintptr_t weakly_referenced : 1; \uintptr_t deallocating : 1; \uintptr_t has_sidetable_rc : 1; \uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
isa union共用體中各個(gè)位的含義
-
nonpointer 指示位,表示是否對(duì)isa指針開啟指針優(yōu)化,值為0表示純isa指針 1表示不止是類對(duì)象的地址,isa中還包含了類信息、對(duì)象的引用計(jì)數(shù)等
-
has_assoc: 關(guān)聯(lián)對(duì)象標(biāo)志位,0沒有,1存在(KVO的實(shí)現(xiàn)原理)
-
has_cxx_dtor :該對(duì)象是否有C++或者Objc的析構(gòu)器,如果有析構(gòu)函數(shù),則需要做析構(gòu)邏輯,如果沒有,則可以更快的釋放對(duì)象
-
shiftcls: 實(shí)際存儲(chǔ)類指針的值。開啟指針優(yōu)化的情況下,在arm64架構(gòu)中有33位用來(lái)存儲(chǔ)類指針,也是基于此,不同的架構(gòu)中,對(duì)于類指針的讀取需要不同的操作,在32位時(shí)期,由于,由于未進(jìn)行isa優(yōu)化,isa是直接指向類對(duì)象的,在64位之后,需要用isa指針與上掩碼(ISA_MASK)才能取到指針,這個(gè)指針指向類對(duì)象。
-
magic :用于調(diào)試器判斷當(dāng)前對(duì)象是真的對(duì)象還是沒有初始化的空間
-
weakly_refrenced: 對(duì)象是否被指向或者曾經(jīng)指向一個(gè)ARC的弱變量,沒有弱引用的對(duì)象,可以更快的釋放
-
dellocating :標(biāo)志對(duì)象是否正在釋放對(duì)象。ing表示進(jìn)行時(shí)
-
has_sidetable_rc:當(dāng)對(duì)象引用計(jì)數(shù)大于0時(shí),則需要借用該變量存儲(chǔ)進(jìn)位。weak的實(shí)現(xiàn)原理,就是使用sidetables。后續(xù)講到內(nèi)存管理的時(shí)候會(huì)對(duì)此進(jìn)行詳細(xì)的講解。
-
extra_rc:當(dāng)表示該對(duì)象的引用計(jì)數(shù)值,實(shí)際上是引用計(jì)數(shù)值減1.例如,如果對(duì)象的引用計(jì)數(shù)為10,那么extra_rc為9,如果引用計(jì)數(shù)大于10,則需要使用到上面的has_sidetable_rc。
用64位存儲(chǔ)了這么多的信息,相當(dāng)?shù)母咝?#xff01;!我當(dāng)初知道這個(gè)點(diǎn)的時(shí)候,嘆為觀止!蘋果真的把這種細(xì)節(jié)這種設(shè)計(jì)做到了極致。蘋果的工程師們真他娘的都是人才,各個(gè)身懷絕技(也可能我作為井底之蛙,沒有了解過這個(gè)技術(shù))。
好,介紹過isa之后,接下來(lái)就該是實(shí)例對(duì)象、類對(duì)象、和元類對(duì)象了。
在arm64架構(gòu)之后,isa通過shiftcls來(lái)指向類對(duì)象的。類對(duì)象也是個(gè)對(duì)象,也有isa指針,它的isa中的shiftcls是指向元類對(duì)象的。元類對(duì)象也有isa,它的shiftcls都指向根類的元類對(duì)象。 網(wǎng)上有一張非常經(jīng)典的圖來(lái)展示了對(duì)象和元類對(duì)象的關(guān)系(這幅圖將貫穿大部分關(guān)于底層原理的博客內(nèi)容)來(lái)看一下:
這個(gè)圖用語(yǔ)言來(lái)描述一下,就是
- 實(shí)例對(duì)象的isa指向的類對(duì)象,類對(duì)象的isa指向元類對(duì)象。元類對(duì)象的isa均指向基類的元類對(duì)象(基類的元類對(duì)象的isa指向自己)。需要特別注意,arm64架構(gòu)后,isa不直接指向,需要先用掩碼取出shift_cls指針,這個(gè)是實(shí)際的指向關(guān)系的指針。
- 類對(duì)象和元類對(duì)象中有superclass指針,普通類對(duì)象的superclass指針指向父類的類對(duì)象,基類類對(duì)象的superclass指向nil?;A(chǔ)對(duì)象的元類對(duì)象的superclass指針指向父類的元類對(duì)象,基類的superclass指針指向父類的類對(duì)象。
寫完這些我都不認(rèn)識(shí)這個(gè)類字了。謹(jǐn)記這幅圖,就能非常好的理解實(shí)例對(duì)象、類對(duì)象、元類對(duì)象之間的關(guān)系了。這個(gè)圖畫的非常好,在后面的runtime的消息轉(zhuǎn)發(fā)流程中還會(huì)用到這幅圖以及這些對(duì)象之間的關(guān)系。
為什么需要類對(duì)象和元類對(duì)象
那為啥要有類對(duì)象和元類對(duì)象呢?類對(duì)象和元類對(duì)象中都放著什么東西呢?
先說(shuō)為什么。我們都知道,我們創(chuàng)建的類,有很多的屬性、協(xié)議和方法,方法又分為實(shí)例方法和類方法。而這些方法的實(shí)現(xiàn)都是統(tǒng)一的,再調(diào)用的過程中,只是參數(shù)的值不同,所以這些方法存一份兒就夠了,沒必要在每個(gè)對(duì)象中都存一份。所以就有了類對(duì)象和元類對(duì)象??梢韵胍幌缕綍r(shí)寫的方法調(diào)用的代碼,假定Person類有一個(gè)實(shí)例方法叫-(void)instanceFunction,有個(gè)類方法+(void)classFunction。我們?cè)谡{(diào)用的時(shí)候是這么調(diào)用的:
- (void)callFunctions{[person instanceFunction]; //使用實(shí)例對(duì)象來(lái)調(diào)用實(shí)例方法[Person classFunction]; //使用類名(其實(shí)就是類對(duì)象)調(diào)用類方法
}
可以很明顯的看出,方法調(diào)用者的不同。而這個(gè)不同就是由類對(duì)象、元類對(duì)象、isa來(lái)實(shí)現(xiàn)的。類對(duì)象中,存儲(chǔ)了這個(gè)類的屬性、協(xié)議和實(shí)例方法。元類對(duì)象中存儲(chǔ)了這個(gè)類的類方法。在方法調(diào)用時(shí),實(shí)例方法通過這個(gè)實(shí)例對(duì)象的isa找到這個(gè)類的對(duì)象,然后在類對(duì)象中查找這個(gè)方法。類方法通過類對(duì)象的isa找到這個(gè)類的元類對(duì)象,在元類對(duì)象中查找這個(gè)方法。通過這樣的描述,結(jié)合上面的圖,就能很好的理解isa指針、類對(duì)象和元類對(duì)象了。
好,簡(jiǎn)單的來(lái)總結(jié)一下今天的內(nèi)容。主要是理清實(shí)例對(duì)象、類對(duì)象、元類對(duì)象之間的關(guān)系,要記住那副圖,保證自己能完整的復(fù)述出他們之間的關(guān)系。isa就相當(dāng)于紐帶,將他們串聯(lián)了起來(lái)。在最后提到了,類對(duì)象和元類對(duì)象是存儲(chǔ)屬性、協(xié)議和方法的,我們會(huì)在runtime中,具體的講解這些信息是如何存儲(chǔ)的。