OC中所有的實例對象、類對象和元類對象中都一個名為isa
的成員變量绎秒,他們通常把它叫isa指針
浦妄,既然是指針,那里面存儲的應(yīng)該就是一個地址见芹。在以前的32位
系統(tǒng)中剂娄,isa
確實就是存儲的一個地址,實例對象的isa
存儲的是其對應(yīng)的類對象的地址玄呛,類對象的isa
存儲的是其對應(yīng)的元類對象的地址阅懦,元類對象的isa
存儲的是根元類對象的地址。
但是在現(xiàn)在的64位系統(tǒng)(arm64架構(gòu))中把鉴,蘋果對isa
做了優(yōu)化故黑,里面除了存儲一個地址外還存儲了很多其他信息。一個指針占8個字節(jié)庭砍,也就是64位,蘋果只用了其中的33位
來存儲地址混埠,其余31位用來存儲其他信息怠缸。下面我們來看下在arm64
架構(gòu)中關(guān)于isa的定義:
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
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_MASK
這種常量我們不用管,這些都是程序在操作isa
的過程中要用到的钳宪,比如我們將isa
和ISA_MASK
進行按位與運算isa & ISA_MASK
就可以得到isa中存儲的地址值揭北。
我們主要關(guān)注一下uintptr_t
類型數(shù)據(jù):
-
nonpointer
:(isa的第0位(isa的最后面那位),共占1位)吏颖。為0表示這個isa
只存儲了地址值搔体,為1表示這是一個優(yōu)化過的isa
。 -
has_assoc
:(isa的第1位半醉,共占1位)疚俱。記錄這個對象是否是關(guān)聯(lián)對象,沒有的話,釋放更快缩多。 -
has_cxx_dtor
:(isa的第2位呆奕,共占1位)。記錄是否有c++的析構(gòu)函數(shù)衬吆,沒有的話梁钾,釋放更快。 -
shiftcls
:(isa的第3-35位逊抡,共占33位)姆泻。記錄類對象或元類對象的地址值。 -
magic
:(isa的第36-41位,共占6位)拇勃,用于在調(diào)試時分辨對象是否完成初始化四苇。 -
weakly_referenced
:(isa的第42位,共占1位)潜秋,用于記錄該對象是否被弱引用或曾經(jīng)被弱引用過蛔琅,沒有被弱引用過的對象可以更快釋放。 -
deallocating
:(isa的第43位峻呛,共占1位)罗售,標(biāo)志對象是否正在釋放內(nèi)存。 -
has_sidetable_rc
:(isa的第44位钩述,共占1位)寨躁,用于標(biāo)記是否有擴展的引用計數(shù)。當(dāng)一個對象的引用計數(shù)比較少時牙勘,其引用計數(shù)就記錄在isa
中职恳,當(dāng)引用計數(shù)大于某個值時就會采用sideTable
來協(xié)助存儲引用計數(shù)。 -
extra_rc
:(isa的第45-63位方面,共占19位)放钦,用來記錄該對象的引用計數(shù)值-1(比如引用計數(shù)是5的話這里記錄的就是4)。這里總共是19位恭金,如果引用計數(shù)很大操禀,19位存不下的話就會采用sideTable
來協(xié)助存儲,規(guī)則如下:當(dāng)19位存滿時横腿,會將19位的一半(也就是上面定義的RC_HALF
)存入sideTable
中颓屑,如果此時引用計數(shù)又+1,那么是加在extra_rc
上耿焊,當(dāng)extra_rc
又存滿時揪惦,繼續(xù)拿出RC_HALF
的大小放入sideTable
。當(dāng)引用計數(shù)減少時罗侯,如果extra_rc
的值減少到了0器腋,那就從sideTable
中取出RC_HALF
大小放入extra_rc
中。綜上所述歇父,引用計數(shù)不管是增加還是減少都是在extra_rc
上進行的蒂培,而不會直接去操作sideTable
,這是因為sideTable
中有個自旋鎖榜苫,而引用計數(shù)的增加和減少操作是非常頻繁的护戳,如果直接去操作sideTable
會非常影響性能,所以這樣設(shè)計來盡量減少對sideTable
的訪問垂睬。