category的實(shí)現(xiàn)原理
編譯之后,category在內(nèi)存中的結(jié)構(gòu)體為 category_t ,編譯器會(huì)把該類的所有分類依次編譯為 category_t 類型的結(jié)構(gòu)體,結(jié)構(gòu)體中包含對象方法列表、類方法列表、協(xié)議列表、屬性列表等,編譯時(shí),并不進(jìn)行結(jié)構(gòu)體合并,在內(nèi)存中是獨(dú)立存在的。
通過runtime的動(dòng)態(tài)特性,在程序運(yùn)行時(shí),將分類結(jié)構(gòu)體中的對象方法、協(xié)議、屬性 合并進(jìn)類對象中,將類方法合并進(jìn)元類對象中,“合并”,實(shí)際上是在內(nèi)存中,對數(shù)據(jù)進(jìn)行內(nèi)存移動(dòng)memmove 和 memcpy
memmove 是根據(jù)傳入的數(shù)據(jù)內(nèi)存大小,從哪開始移動(dòng),中間是有一個(gè)判斷的,是向左挪動(dòng)還是向右挪動(dòng)(可以保證數(shù)據(jù)的完整性);而 memcpy 不判斷,它是從最小的內(nèi)存開始逐一復(fù)制移動(dòng),將分類中的數(shù)據(jù)插入到原來數(shù)據(jù)前面
進(jìn)行內(nèi)存移動(dòng)的時(shí)候,memmove 不會(huì)造成內(nèi)存覆蓋;從新的地方進(jìn)行內(nèi)存移動(dòng)的時(shí)候,使用 memcpy 比較合適,因?yàn)樗恍枰軘?shù)據(jù)是否會(huì)內(nèi)存覆蓋
category的加載處理過程
1.通過RunTime加載某個(gè)類的所有Category數(shù)據(jù)
2.把所有的category方法,屬性,協(xié)議數(shù)據(jù)合并到一個(gè)大數(shù)組中,后面參與的category數(shù)據(jù),會(huì)被加到數(shù)組前面
3.將合并的分類數(shù)據(jù)(方法,屬性,協(xié)議),插入到原來數(shù)據(jù)的前面
Category 和 Extension(匿名分類) 的區(qū)別
Class-Extension 在編譯階段,它的數(shù)據(jù)就已經(jīng)加載到了類信息中(類 對象、元類對象),而Class-Category是在運(yùn)行時(shí),才將數(shù)據(jù)合并到類信息中。
Category 中l(wèi)oad方法什么時(shí)候調(diào)用?load方法能繼承嗎?
load方法會(huì)在runtime加載類和分類的時(shí)候調(diào)用。
調(diào)用順序:1.先調(diào)用類的+load,按照編譯先后順序調(diào)用(先編譯,先調(diào)用)調(diào)用子類的+load之前會(huì)先調(diào)用父類的+load;2.再調(diào)用分類的+load,按照編譯先后順序調(diào)用(先編譯,先調(diào)用)
load能繼承,但是一般情況下不主動(dòng)去調(diào)用load方法,都是讓系統(tǒng)自己去調(diào)用。
load,initialize的方法區(qū)別是什么?它們在Category中的調(diào)用順序?以及出現(xiàn)繼承時(shí)他們之間的調(diào)用過程?
調(diào)用方式
load,根據(jù)函數(shù)地址直接調(diào)用(IMP)。initialize,通過objc_msgSend調(diào)用,類第一次接收到消息的時(shí)候調(diào)用
調(diào)用時(shí)機(jī)
load,runtime加載類、分類的時(shí)候調(diào)用(只調(diào)用一次)。initialize,類第一次收到消息的時(shí)候調(diào)用,每個(gè)類只會(huì)調(diào)用一次(父類的+initialize 可能會(huì)被調(diào)用多次)
調(diào)用順序
load,先調(diào)用類的+load(先編譯先調(diào)用,調(diào)用子類load之前先調(diào)用父類的load),再調(diào)用分類的+load。initialize,先調(diào)用父類的+initialize,再調(diào)用自己的+initialize
繼承出現(xiàn)時(shí),調(diào)用子類load之前先調(diào)用父類的load,先調(diào)用父類的+initialize,再調(diào)用自己的+initialize