1.分類的加載處理過程
- 通過Runtime加載某個類的所有Category數(shù)據(jù)
- 把所有Category的方法、屬性泽本、協(xié)議數(shù)據(jù)岔激,合并到一個大的數(shù)組中(這個大的數(shù)組是一個二維數(shù)組)漾月,后面參與編譯的Category數(shù)據(jù)會在數(shù)組的前面
- 將合并后的分類數(shù)據(jù)(方法熊泵、屬性、協(xié)議)箕般,插入到類的原來的數(shù)據(jù)的前面耐薯。
因為以上所以分類的方法會覆蓋類里面的相同名字的方法,但是不是真正的覆蓋,只是最先遍歷到的是分類里面的方法
2.Category的實現(xiàn)原理
- Category編譯之后的底層結(jié)構(gòu)是struct category_t,里面存儲著分類的對象方法曲初、類方法体谒、屬性、協(xié)議信息
- 在程序運行的時候,runtime會將Category的數(shù)據(jù),合并到類信息中(類對象臼婆、元類對象中)
3. Category和Class Extension的區(qū)別是什么?
- Class Extension在編譯的時候,他的數(shù)據(jù)就已經(jīng)包含在類信息中(編譯期已經(jīng)合并到類信息中了)
- Category是在運行時,才會將數(shù)據(jù)合并到類信息中
4. Load方法的調(diào)用
- load方法會在runtime加載類,分類的時候調(diào)用(也就是在程序運行的時候調(diào)用)
- 每個類,分類的load方法在程序運行的過程中只會調(diào)用一次
- load方法的調(diào)用順序
1. 先調(diào)用類的load方法 * 按照編譯先后順序調(diào)用(先編譯,先調(diào)用) * 調(diào)用子類的load方法之前會先調(diào)用父類的load方法 2. 再調(diào)用分類的load方法 * 按照編譯先后順序調(diào)用(先編譯,先調(diào)用)
- +load方法可以繼承,但是一般不會主動調(diào)用(系統(tǒng)調(diào)用+load方法的時候沒有用到消息發(fā)送機制,而是直接找到的+load方法的地址來直接調(diào)用的)
5. +Initialize的調(diào)用
- +Initialize 會在類第一次接收到消息的時候調(diào)用
- 先調(diào)用父類的+ initalize,再調(diào)用子類的+ initialize,但是它也遵循消息發(fā)送機制,即如果分類中實現(xiàn)了+ initalize,那么就會覆蓋原來類的+ initalize,原來類的+ initalize就不會被調(diào)用.
- 先初始化父類,再初始化子類,每個類只會初始化一次
*+ initalize的調(diào)用再源碼中,會在每次對象調(diào)用方法前去判斷,當前類的+ initalize方法是否被調(diào)用過,如果沒有被調(diào)用過就判斷他的父類的+ initalize方法是否被調(diào)用,沒有調(diào)用就先調(diào)用父類的+ initalize,再調(diào)用自己的+ initalize方法,如果都調(diào)用過了接下來走正常的消息發(fā)送機制.
- initalize和+load的很大區(qū)別是,+ initalize查找過程后的調(diào)用是通過objc_msgSend進行調(diào)用的,所以有以下特點
* 如果子類沒有實現(xiàn)+ initalize,會調(diào)用父類的+ initalize(所以父類的+ initalize可能會被調(diào)用多次) * 如果分類實現(xiàn)了+ initalize,就會覆蓋類本身的+ initalize的調(diào)用
- initalize和+load的很大區(qū)別是,+ initalize查找過程后的調(diào)用是通過objc_msgSend進行調(diào)用的,所以有以下特點
6. Block
- block本質(zhì)
- block本質(zhì)上是一個OC對象,它內(nèi)部也有一個isa指針
- block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象
- block捕獲變量
- 局部變量(atuo,static)的捕獲,auto變量的捕獲是值傳遞,static捕獲是地址傳遞,這樣捕獲的原因是auto變量可能會銷毀,所以只能是值傳遞
-
全局變量,block內(nèi)部不會捕獲,因為全局變量,任何地方都能訪問,所以不需要捕獲
- self 也會被block所捕獲,self也是局部變量,因為self本身就是地址,所以他也是值傳遞
- block 的類型
- 沒有訪問auto變量的是global類型
- 訪問了auto變量是stack類型
-
stack類型調(diào)用copy后變?yōu)閙alloc類型
- block的copy
在ARC環(huán)境下,編譯器會鞥局情況自動將棧上的block復(fù)制到堆上,比如以下情況- block作為函數(shù)的返回值時
- 將block賦值給__strong指針時
- block作為Cocoa API中方法名含有usingBlock的方法的參數(shù)時
- block作為GCD API的方法參數(shù)時
-
block對象類型的auto變量的捕獲
- __block修飾符
- __block可以用于解決block內(nèi)部無法修改auto變量值的問題
- __block 不能修飾全局變量,靜態(tài)變量
- 編譯器會將__block修飾的變量包裝成一個對象
- 被__block 修飾的對象類型
- 當__block變量在棧上時,不會對指向的對象產(chǎn)生強引用
- 當__block變量被copy到堆上時
- 會調(diào)用__block變量內(nèi)部的copy函數(shù)
- copy 函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)
- _Block_object_assign函數(shù)會根據(jù)所指向的對象的修飾符(__strong,__weak,__unsafe_unretaind)做出相應(yīng)的操作,形成強引用或者弱引用(注意: 這里僅限ARC時會retain,MRC時不會retain)
- 如果__block變量從堆上移除
- 會調(diào)用__block變量內(nèi)部的dispose函數(shù)
- dispose 函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)
- _Block_object_dispose函數(shù)會自動釋放指向的對象(release)
- block的循環(huán)引用
- 可以用___weak或者__unsafe_unretianed來解決循環(huán)引用的問題
- 他們的不同點
__weak: 不會產(chǎn)生強引用,指向的對象銷毀時,會自動讓指針置為nil __unsafe_unretianed不會產(chǎn)生強引用,不安全,因為當指向的對象銷毀時,指針不會置為nil,指針存儲的地址不變,會產(chǎn)生野指針的問題
- 使用__block也能解決循環(huán)引用的問題,但是必須要調(diào)用block,并且要在block內(nèi)部將變量置為nil,因為__block修飾的變量會被包裝成一個對象,這個對象內(nèi)部對變量有一個強引用,對象本身對被包裝的__block對象有一個強引用,會形成一個三角引用,需要調(diào)用block后,并且在block的調(diào)用內(nèi)部將變量置為nil,才會打破這種循環(huán)引用.
- block的屬性修飾詞為什么是copy?使用block有哪些注意?
- block一旦沒有進行copy操作,就不會在堆上,就不能自己控制他的生命周期
- 使用注意: 要注意循環(huán)引用