KVO 原理
里氏替換
- 利用runtime 動態(tài)生成一個子類A,且修改 instacne a對象的isa指向該全新子類
- NSKVONotifying_A 的屬性被修改時,子類的set方法內(nèi)部調(diào)用
willChangeValueForKey -> 原本的set實現(xiàn) -> didChangeValueForKey - didChangeValueForKey會調(diào)用監(jiān)聽器的observeValueForKeyPath:ofObject:change:context:監(jiān)聽方法
動態(tài)生成的 NSKVONotifying_A 結(jié)構(gòu)體:有isa、superClass、setAge方法、class方法
setAge 方法:對觀察的變量重寫的set方法
class 方法:重寫,以隱藏自己的存在(返回再上一層),使 A 的實例調(diào)用 class 時仍返回 A 而非真實的 class NSKVONotifying_A
_isKVOA = YES;
dealloc 收尾方法重寫
- 手動觸發(fā)KVO
調(diào)用willChangeValueForKey 和 didChangeValueForKey,缺一不可。
[p1 willChangeValueForKey:@“age”];
// do something
[p1 didChangeValueForKey:@“age”];
- 取消kvo
automaticallyNotifiesObserversForKey 返回NO
KVC 原理
- 先調(diào)用 setKey _setKey ,找了 直接調(diào)用
- 未找到 則 查看 accessInstanceVariablesDirectly 此方法是否可以直接訪問成員變量 (默認=YES)
- 可訪問 按照 _key _isKey key isKey順序取賦值,未找到報錯 NSUnknownKeyException
- valueForKey類似
分類原理
- 分類文件在編譯后的底層結(jié)構(gòu)是 struct category_t,里面有分類的對象方法,類方法,屬性,協(xié)議信息
- 在程序運行階段,runtime將category的數(shù)據(jù),合并到類信息中(類對象,元類對象)
- 與類擴展不同在于 一個編譯時合并ro,一個運行時合并rw
- 后編譯的分類方法,在消息查找時會覆蓋前面的同名方法(原方法也還在)
分類添加弱引用對象 weak:加個obj,obj弱引用該對象
load 和 initialize
load
- +load 在 runtime 加載類,分類時調(diào)用
- 每個類,分類的 +load 運行時只調(diào)用一次,直接根據(jù)load方法地址調(diào)用,不走objc_msgSend
- 先調(diào)用類的 +load,先父類 再 子類
- 再調(diào)用分類的load,先編譯 先調(diào)用
initialize
- +initialize 在類第一次收到消息調(diào)用
- 先調(diào)用父類的 +initialize ,再調(diào)用子類 initialize
- +initialize通過 objc_msgSend調(diào)用,如果子類未實現(xiàn) +initialize,會導(dǎo)致父類 initialize 多次調(diào)用
關(guān)聯(lián)對象
- 所有的關(guān)聯(lián)對象由全局對象 AssociationsManager管理,內(nèi)部有個hashMap map
- map使用 對象地址做為key ,ObejctAssociationMap 作為value
- ObejctAssociationMap 內(nèi)部也是屬性作為 key,ObejctAssociation作為value
- ObejctAssociation內(nèi)部保存 策略 和 value
NSObject占用多少內(nèi)存
16字節(jié),因為對象最少16字節(jié),一個指針是8字節(jié)(64位)
內(nèi)存對齊小結(jié)
- 前面的地址必須是后面的地址正數(shù)倍,不是就補齊
- 整個Struct的地址必須是最大字節(jié)的整數(shù)倍
在繼承條件下為
Person:isa 8字節(jié) + age 4字節(jié) + 內(nèi)存對齊 = 16字節(jié)
Student:isa 8字節(jié) + age 4字節(jié) + no 4字節(jié) (已對齊)= 16字節(jié)