KVO實現原理
KVO基本原理:
- 1 kvo是基于runtime機制實現的
- 2 當某個類的屬性對象第一次被觀察時,系統(tǒng)就會在運行期動態(tài)的創(chuàng)建該類的一個派生類,在這個派生類中重寫基類中任何被觀察屬性的setter方法,派生類在被重寫的setter方法內實現真正的通知機制
- 3 如果原類為Person,那么生成的派生類名為NSKVONotifying_Person
- 4 每個類對象中都有一個isa指針指向當前類,當一個類對象的第一次被觀察,那么系統(tǒng)會偷偷將isa指針指向動態(tài)生成的派生類,從而在給被監(jiān)控屬性賦值時執(zhí)行的派生類的setter方法
- 5 鍵值觀察通知依賴于NSObject的兩個方法:willChangeValueForKey:和didChangeValueForKey:在一個被觀察屬性發(fā)生改變之前,willChangeValueForKey:會被調用,這就會記錄舊的值,而當改變發(fā)生后,didChangeValueForKey:會被調用,繼而observeValueForKey:ofObject:change:context: 也會被調用。
KVO深入原理:
1.Apple使用了isa混寫(isa-swizzling)來實現KVO,當觀察對象A時,KVO機制動態(tài)創(chuàng)建一個新的名為:NSKVONotifying_A的新類,該類繼承自對象A的本類,且KVO為NSKVONotifying_A重寫觀察屬性的setter方法,setter方法會負責在調用原setter方法之前和之后,通知所有觀察對象屬性值的更改情況。
2 NSKVONotifying_A類剖析:在這個過程,被觀察對象的isa指針從只想原來的A類,被KVO機制修改為指向系統(tǒng)新創(chuàng)建的自雷NSKVONotifying_A類,來實現當前類屬性值改變的監(jiān)聽。
3 所以當我們從應用層面上來看,完全沒有意識到有新的類的除夕拿,這是系統(tǒng)“隱藏”了對KVO的底層實現過程,讓我們誤以為還是原來的類。但是此時如果我們創(chuàng)建一個新的名為NSKVONotifying_A的類,就會發(fā)現系統(tǒng)運行到注冊KVO的那段代碼時程序就崩潰,因為系統(tǒng)在注冊監(jiān)聽的時候動態(tài)創(chuàng)建了名為NSKVONotifying_A的中間類,并指向這個中間類了。
4 (isa指針的作用:每個對象都有isa指針,指向該對象的類,它告訴Runtime系統(tǒng)這個對象的類是什么。所以對象注冊為觀察者時,isa指針指向新子類,那么這個被觀察的對象就神奇的變成新子類的對象(或實例)了)因而在該對象上對setter的調用就會調用已重寫的setter,從而激活鍵值通知機制。
5 子類setter方法剖析:KVO的鍵值觀察通知依賴于NSObject的兩個方法:willChangeValueForKey:和didChangeValueForKey:,在存取數值的齊納后分別調用2個方法,被觀察屬性發(fā)生改變之前,willChangeValueForKey:被調用,通知系統(tǒng)該keyPath的屬性值即將變更,當改變發(fā)生后,didChangeValueForKey:被調用,通知系統(tǒng)該keyPath的屬性值已經變更,之后observeValueForKey:ofObject:change:context: 也會被調用,且重寫觀察屬性的setter方法這種集成法師的注入實在運行時而不是在編譯時實現的。
640.jpeg
消息轉發(fā)機制原理
消息轉發(fā)機制基本分為三個步驟:
1。動態(tài)方法解析
2。備用接受者
3。完整轉發(fā)

week屬性
weak實現原理
Runtime維護了一個weak表,用于存儲指向某個對象的所有weak指針,weak表其實是一個hash(哈希)表,key時所指對象的地址,Value時weak指針的地址(這個地址的值時所指對象的地址)數組。
1。初始化時:runtime會調用objc_initWeak函數,初始化一個新的weak指針只想對象的地址。
2。添加引用時,objc_initWeak函數會調用objc_storeWeak()函數,objc_storeWeak()的作用是更新指針指向,創(chuàng)建對應的弱引用表。
3。釋放時,調用clearDeallocating函數,clearDeallocating函數首先根據對象地址獲取所有weak指針地址的數組,然后遍歷這個數組把其中的數據設為nil,最后把這個entry從weak表中刪除,最后清理對象的記錄。
實現weak后,為什么對象釋放后會自動為nil
runtime對注冊的類,會進行布局,對于weak對象會放入一個hash表中,用weak指向的對象內存地址作為key,當此對象的應用能夠技術為0 的時候會dealloc,加入weak指向的對象內存地址為a, 那么就會以a為鍵,在這個weak表中搜索,找到所有以a為鍵的weak對象,從而設置為nil。
當weak引用指向的對象被釋放時,又是如何去處理weak指針的呢
1調用objc_release2因為對象的引用計數為0,所以執(zhí)行dealloc3 在dealloc中,調用了_objc_rootDealloc函數4在_objc_rootDealloc中,調用了object_dispose函數5調用objc_destructInstance6最后調用objc_clear_deallocating:詳細過程如下:a.從weak表中獲取廢棄對象的地址為鍵值的記錄b將包含在記錄中的所有附有weak修飾符變量的地址,賦值為nil c將weak表中該記錄刪除 d從引用計數表中刪除廢棄對象的地址為鍵值的記錄。
如何優(yōu)化過于臃腫的Controller
- 1、將網絡請求抽象到單獨的類中
方便在基類總處理公共邏輯 方便在基類中處理緩存邏輯,以及其他一些公共邏輯 方便作對象的持久化
- 2、將界面的封裝抽象到專門的類中
構造專門的UIview的自雷,來負責這些控件的拼裝,這是最徹底和優(yōu)雅的方式,不過稍微麻煩一些的是,你需要把這些空間的事件回調先接管,再都一一暴露回controller
- 3 構造viewmodel
借鑒MVVM,具體做法將VIewConttroller給view傳遞數據這個過程,抽象成構造viewmodel的過程。
- 4 專門構造存儲類
專門來出來本地數據的存取
- 5 整合常量
項目中網絡層如何做安全處理
1、盡量使用https
https可以過濾掉大部分的安全問題,https在證書申請,服務器配置,性能優(yōu)化,客戶端配置上都需要投入精力,所以缺乏安全意識的開發(fā)人員容易跳過https,或者拖到以后遇到問題再優(yōu)化,https除了性能優(yōu)化麻煩一些以外其他都比想象中的簡單,如果沒有精力優(yōu)化性能,至少在注冊登錄模塊需要啟用https,這部分業(yè)務對性能要求比較低。
2、不要傳輸明文密碼
不知道現在還有多少app后臺是明文存儲密碼的。無論客戶端,server還是網絡傳輸都要避免明文密碼,要使用hash值??蛻舳瞬灰鋈魏蚊艽a相關的存儲,hash值也不行。存儲token進行下一次的認證,而且token需要設置有效期,使用refresh token去申請新的token
3、post并不比get安全
事實上,post和get一樣不安全,都是明文,參數放在queryString或者body沒有任何安全上的差別,在http的環(huán)境下,使用post或者get都需要做加密和簽名處理。
4、不要使用301跳轉
301跳轉很容易被http劫持攻擊,移動端http使用301比桌面端更危險,用戶看不到瀏覽器地址,無法察覺到被重定向到了其他地址,如果一定要使用,確保跳轉發(fā)生在https的環(huán)境下,而且https做了證書綁定校驗。
5、http請求都帶上MAC
所有客戶端發(fā)出的請求,無論是查詢還是寫操作,都帶上MAC(Message Authentication Code)。Mac不但能保證請求沒有被篡改,還能保證請求確實來自你的合法客戶端。當然前提是你客戶端的key沒有被泄露,如何保證客戶端key的安全是另一話題,mac值的計算可以簡單的處理為hash(request+params+key)。帶上mac后,服務器就可以過濾掉絕大部分的非法請求,mac雖然帶有簽名的功能,和rsa證書的電子點名方式卻不一樣,原因是mac簽名和簽名驗證使用的是同一個key。而rsa是使用私鑰簽名,公鑰驗證,mac的簽名并不具備法律效應。
6、http請求使用臨時密鑰
高延遲的網絡環(huán)境下,不經優(yōu)化https的體驗確實會明顯不如http。在不具備https條件或對網絡性能要求較高且缺乏https優(yōu)化經驗的場景下,http的流量也應該使用AES進行加密。AES的密鑰可以由客戶端來臨時生成,不過這個臨時的AES key需要使用服務器的公鑰進行加密,確保只有自己的服務器才能解開這個請求的信息,當然服務器的response也需要使用同樣的AES key進行加密。由于http的應用場景都是由客戶端發(fā)起,服務器響應,所以這種由客戶端單方生成密鑰的方式可以一定程度上便捷的保證通信安全。
7、AES使用CBC模式
不要使用ECB模式,記得設置初始化向量,每個block加密之前要和上個block的秘文進行運算。
main()之前的過程有哪些?
main之前的加載過程:
1、dyld開始將程序二進制文件初始化
2、交有ImageLoader讀取image,其中包含了我們的類、方法等各種符號
3、由于runtime 向dyld 綁定了回調,當image加載到內存后,dyld會通知runtime進行處理
4、runtime 接手后調用map_images做解析和處理
5、接下來load_images 中調用call_load_methods方法,遍歷所有加載進來的Class,按繼承層次依次調用Class的+load和其他Category的+load方法
6、至此 所有的信息都被加載到內存中
7、最后dyld調用真正的main函數
注意:dyld會緩存上一次把信息加載內存的緩存,所以第二次比第一次啟動快一點
