在上一篇文章中,我們了解了objc
和dyld
的關聯(lián),那么關聯(lián)之后怎么對類進行加載的呢耘子, 本篇將對類的加載進行探索和分析。
在上一篇文章中我們通過objc_init
里面的_dyld_objc_notify_register
來到了_read_images
我們點到_read_images
里面去球切,把項目跑起來谷誓,然后打斷點調試,看看走哪些代碼
來到3597
行的時候吨凑,我們在里面判斷一下讀取自己的類捍歪。
因為這里還會讀取很多系統(tǒng)的類,而系統(tǒng)的類我們又不好操控怀骤,所以在readClass
里面费封,我們寫上這么一段代碼,讀取到我們自己的類蒋伦,,讓自己創(chuàng)建的類進來焚鹊。
來到我們自己類之后接著往下走痕届。通過在判斷條件里面打斷點的方式,判斷每個if
下面有沒有走末患。
接著來到realizeClassWithoutSwift
實現(xiàn)當前的類研叫,我們在自己研究的類這里打個斷點,發(fā)現(xiàn)沒有進來璧针,這是啥情況嚷炉,一般人又玩不進來了。 其實蘋果也給了注釋 Realize non-lazy classes (for +load methods and static instances)
大概意思是需要實現(xiàn)非懶加載的類才可以進去探橱,用+load
方法來實現(xiàn)申屹。那么我們在LGPerson
里面加一個load
方法試試
加完之后我們再來運行一下看看進沒進來
果然進來了,從而得出加了load
方法后讓類提前實現(xiàn)成為非懶加載類隧膏,那么懶加載類在什么時候呢哗讥,我們把load方法注釋來到realizeClassWithoutSwiftbt
打個斷點,然后打印一下堆棧
在我們[LGPerson alloc];
其實是進行了一次消息的發(fā)送_objc_msgSend_uncached
胞枕,也就是當你要用到這個類的時候才會去實現(xiàn)這個類杆煞,這樣大大減少了程序啟動前的內存加載。
我們用一張PPT來總結下懶加載和非懶加載的區(qū)別:
類的加載
明白了懶加載和非懶加載的區(qū)別之后,回到_read_images
進來的斷點里面决乎,斷點進來后我們接著進到realizeClassWithoutSwift
里面去队询,這個方法也是本篇文章研究的重點,因為在類相關的判斷里面都有realizeClass
實現(xiàn)類的方法构诚。
從而能看出來類就是在realizeClassWithoutSwift
這個里面實現(xiàn)的蚌斩,其實之前在lookupimp
慢速查找的時候也提到過,那我們點進去看一下到底是怎么實現(xiàn)的
首先在開始前還是加一句只研究本類的代碼唤反, 排除其它類和元類凳寺,同時在自己類里面加了一些方法和屬性,方便研究彤侍。
加好之后肠缨,我們回到realizeClassWithoutSwift
,接下來再斷點定到自己的類盏阶,接著往下看
在這個地方從我們的MachO
里面讀取到了我們的data
晒奕,按照一定的格式轉換成class_ro_t
賦值給了臨時變量ro
,
拿到ro
之后判斷是否為元類名斟,顯然不是脑慧,所以來到else
里面,來到里面之后第一句代碼rw = objc::zalloc<class_rw_t>();
申請和開辟class_rw_t
砰盐,此時rw
還只是剛創(chuàng)建闷袒,是空的,然后set_ro
把ro
放到了rw
里面岩梳,再setData
把rw
給了LGPerson
囊骤,設置完了之后LGPerson
還是空的,接著往下走
走到這兩句就意味著把我們類信息確定好之后冀值,再確定父類和元類也物,從LGPerson 類信息 -> 父類 -> 元類
就是為了確定繼承鏈關系,實現(xiàn)完了之后就會來到isa
的設置列疗,完了之后就把整個繼承鏈設置好了滑蚯。
接著往下走,設置一些其它信息抵栈,比如setHasCxxDtor
告材,設置Cxx
,這就是為什么類里面有這么個Cxx
的方法竭讳,這是系統(tǒng)默認設置的创葡。
設置完了之后來到methodizeClass
這句重點,點進去绢慢,然后在methodizeClass
繼續(xù)做一個判斷灿渴,只研究當前自己的類洛波,因為在realizeClassWithoutSwift
遞歸了父類和元類,所以methodizeClass
會進來父類和元類骚露,寫這個判斷就是為了排除別的類蹬挤。
來到自己的類之后,把rw
棘幸,ro
焰扳,rwe
拿了出來
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
然后把baseMethods
拿出來
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
if (rwe) rwe->methods.attachLists(&list, 1);
}
然后走到prepareMethodLists
,看下里面做了什么操作
看到下面有一句fixupMethodList
误续,注釋的意思是修正方法list
吨悍,應該是對方法排序的意思,我們點進去看一下
果然蹋嵌,把這里把方法的名字進行一系列的處理之后開始sort
排序
根據sel
的名字來進行排序育瓜,排序前和排序后分別來打印一下
注意,這里是根據名字的地址大小進行排序的栽烂,而不是文字的字符串大小排序的躏仇。
把順序排好之后,來到rwe
腺办,我斷點走完之后發(fā)現(xiàn)rwe始終為NULL焰手,這是啥情況,這個地方讓很多人百思不得其解怀喉,在后面我會為大家揭曉书妻。我們先來看一下methodizeClass
這個方法上面的注釋Attach categories
附加分類,說明跟分類有關系躬拢,既然說到分類那就看看分類里面到底有些什么東西驻子,可以通過Clang來看,我這里就直接搜索category_t
來看
看到category_t
有classMethods
估灿,protocols
等等,那么這里面的這些東西怎么加到我們的類里面去的呢缤剧,帶著這個問題繼續(xù)來探索:
我們在methodizeClass
往下翻會看到分類相關的東西
在1480
行有一個attachToClass
馅袁,非常好,我們點進去看到底是怎么加到類里面去的
老規(guī)矩荒辕,在前面還是加上只研究本類的判斷汗销, 看到category_list &list = it->second;
,然后在下面進行實現(xiàn)分類的判斷調用了attachCategories
抵窒, 如果if (flags & ATTACH_CLASS_AND_METACLASS)
滿足弛针,則說明類和分類都實現(xiàn)了,如果不滿足李皇,則說明主類沒有實現(xiàn)load
方法,而分類實現(xiàn)了load
方法需要進行加載了削茁,就會來到else
下面宙枷。
我們點到attachCategories
里面去看看
來到這里面之后我們意外的發(fā)現(xiàn) auto rwe = cls->data()->extAllocIfNeeded();
,rwe
原來就在這里進行創(chuàng)建或者獲取茧跋,看來要對本類進行添加分類信息的時候才會處理這個rwe
慰丛。那么還有哪些地方有用到嗎,我們全局搜索
通過全局搜索能夠看出rwe
除了在添加分類以外瘾杭,還在除了系統(tǒng)外的addMethod
诅病,addProtocol
,addProperty
這些地方用到過粥烁,說白了正如WWDC
里面所說的只有對原始的內存進行處理和修改的時候我們才會用到rwe
贤笆。
明白了rwe
之后,我們再回attachCategories
接著打一個斷點進入LGPerson
當前要研究的類讨阻,然后開始遍歷cats_count
可以看到這里來到的是LGPerson
的分類LGA
芥永,然后拿到分類的method_list
拿到分類的方法列表之后,判斷mcount
是否等于ATTACH_BUFSIZ
变勇,表示最大修改次數(shù)為64次恤左, 如果不等于則mlists[64 - 1] = mlist
進行倒序插入
if (mcount > 0) {
prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
if (flags & ATTACH_EXISTING) flushCaches(cls);
}
倒序插入完了之后會來到
if (mcount > 0) {
prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
if (flags & ATTACH_EXISTING) flushCaches(cls);
}
到這一步,我們數(shù)據已經準備好了搀绣,那么到底是怎么加載到我們類里面去的呢飞袋,還有在什么時機加進去的,這將在下篇文章進行展開分析链患。 本來這篇文章只講類的加載的巧鸭,結果摸到分類來了,也算是提前給下一篇文章做個預告
未完待續(xù)麻捻。纲仍。。贸毕。