記錄

一些小的tips

網(wǎng)絡(luò)七層圖
網(wǎng)絡(luò)七層圖

HTTPS建立連接
HTTPS建立連接步驟
  • 步驟 1: 客戶端通過發(fā)送 Client Hello 報(bào)文開始 SSL 通信。報(bào)文中包含客戶端支持的 SSL 的指定版本、加密組件(Cipher Suite)列表(所使用的加密算法及密鑰長(zhǎng)度等)
  • 步驟 2: 服務(wù)器可進(jìn)行 SSL 通信時(shí),會(huì)以 Server Hello 報(bào)文作為應(yīng)答。和客戶端一樣,在報(bào)文中包含 SSL 版本以及加密組件。服務(wù)器的加密組件內(nèi)容是從接收到的客戶端加密組件內(nèi)篩選出來的。
  • 步驟 3: 之后服務(wù)器發(fā)送 Certificate 報(bào)文。報(bào)文中包含公開密鑰證書。
  • 步驟 4: 最后服務(wù)器發(fā)送 Server Hello Done 報(bào)文通知客戶端,最初階段的 SSL 握手協(xié)商部分結(jié)束。
  • 步驟 5: SSL 第一次握手結(jié)束之后,客戶端以 Client Key Exchange 報(bào)文作為回應(yīng)。報(bào)文中包含通信加密中使用的一種被稱為 Pre-master secret 的隨機(jī)密碼串。該報(bào)文已用步驟 3 中的公開密鑰進(jìn)行加密。
  • 步驟 6: 接著客戶端繼續(xù)發(fā)送 Change Cipher Spec 報(bào)文。該報(bào)文會(huì)提示服務(wù)器,在此報(bào)文之后的通信會(huì)采用 Pre-master secret 密鑰加密。
  • 步驟 7: 客戶端發(fā)送 Finished 報(bào)文。該報(bào)文包含連接至今全部報(bào)文的整體校驗(yàn)值。這次握手協(xié)商是否能夠成功,要以服務(wù)器是否能夠正確解密該報(bào)文作為判定標(biāo)準(zhǔn)。
  • 步驟 8: 服務(wù)器同樣發(fā)送 Change Cipher Spec 報(bào)文。
  • 步驟 9: 服務(wù)器同樣發(fā)送 Finished 報(bào)文。
  • 步驟 10: 服務(wù)器和客戶端的 Finished 報(bào)文交換完畢之后,SSL 連接就算建立完成。當(dāng)然,通信會(huì)受到 SSL 的保護(hù)。從此處開始進(jìn)行應(yīng)用層協(xié)議的通信,即發(fā)送 HTTP 請(qǐng)求。
  • 步驟 11: 應(yīng)用層協(xié)議通信,即發(fā)送 HTTP 響應(yīng)。
  • 步驟 12: 最后由客戶端斷開連接。斷開連接時(shí),發(fā)送 close_notify 報(bào)文。上圖做了一些省略,這步之后再發(fā)送 TCP FIN 報(bào)文來關(guān)閉與 TCP 的通信。
  • 在以上流程中,應(yīng)用層發(fā)送數(shù)據(jù)時(shí)會(huì)附加一種叫做 MAC(Message Authentication Code)的報(bào)文摘要。MAC 能夠查知報(bào)文是否遭到篡改,從而保護(hù)報(bào)文的完整性。

常量和預(yù)處理指令
  • 如果用預(yù)處理指令定義常量,定義出來的常量不含類型信息,編譯器只是會(huì)在編譯前據(jù)此執(zhí)行查找和替換操作,即使有人重新定義了常量值編譯器也不會(huì)產(chǎn)生警告信息這將導(dǎo)致應(yīng)用程序中的常量值不一致
  • 在實(shí)現(xiàn)文件中使用static const來定義,只在編譯單元類可見的常量值,這種常量沒有在全局符號(hào)表中
  • 在頭文件中使用extern來聲明全局常量,并在相關(guān)實(shí)現(xiàn)文件中定義其值。這種常量要出現(xiàn)在全局符號(hào)表中。所以名稱要加以區(qū)分通常使用類名做前綴

Controller的生命周期
  • loadView:加載view
  • viewDidLoad:view加載完畢
  • viewWillAppear:控制器的view將要顯示
  • viewWillLayoutSubviews:控制器的view將要布局子控件
  • viewDidLayoutSubviews:控制器的view布局子控件完成
    這期間系統(tǒng)可能會(huì)多次調(diào)用viewWillLayoutSubviews 、 viewDidLayoutSubviews 倆個(gè)方法
  • viewDidAppear:控制器的view完全顯示
  • viewWillDisappear:控制器的view即將消失的時(shí)候
    這期間系統(tǒng)也會(huì)調(diào)用viewWillLayoutSubviews 、viewDidLayoutSubviews 兩個(gè)方法
  • viewDidDisappear:控制器的view完全消失的時(shí)候

用枚舉表示狀態(tài),選項(xiàng),狀態(tài)碼
  • 用枚舉表示狀態(tài)機(jī)的狀態(tài),傳遞給方法的選項(xiàng),以及狀態(tài)碼等值,給這些值起個(gè)易懂的名字
  • 如果把傳遞給某個(gè)方法的選項(xiàng)表示為枚舉類型,而多個(gè)選項(xiàng)又可以同時(shí)使用,那么就將各個(gè)選項(xiàng)定義成2的冪,以便通過按位或操作將其組合起來
  • 使用 NS_ENUM 與 NS_OPTIONS宏來定義枚舉類型,并指明其底層數(shù)據(jù)類型,這樣做可以確保枚舉使用開發(fā)者所選擇的底層數(shù)據(jù)類型實(shí)現(xiàn)出來的,而不會(huì)采用編譯器所選的類型
  • 在處理枚舉類型的switch語(yǔ)句中不要實(shí)現(xiàn)default分支。這樣的話加入新枚舉之后,編譯器就會(huì)提示開發(fā)者:switch語(yǔ)句并未處理所有的枚舉

精簡(jiǎn)initialize 與 load 的實(shí)現(xiàn)代碼
  • 在加載階段,如果類實(shí)現(xiàn)了load方法,那么系統(tǒng)就會(huì)調(diào)用到它。分類里也可以定義此方法,類的load方法要比分類中的先調(diào)用。與其他方法不同,load方法不參與覆寫機(jī)制。
  • 首次使用某個(gè)類之前,系統(tǒng)會(huì)像其發(fā)送initialize消息。由于此方法遵從普通的覆寫規(guī)則,所有通常應(yīng)該在里面判斷當(dāng)前要初始化的是哪個(gè)類。
  • load 與 initialize 方法都應(yīng)該實(shí)現(xiàn)的精簡(jiǎn)一些,這里有助于保持應(yīng)用程序的響應(yīng)能力,也能減少引入依賴環(huán)的幾率。
  • 無法在編譯期設(shè)定的全局常量,可以放在initialize方法里初始化

NSTimer會(huì)保留其目標(biāo)對(duì)象
  • NSTimer對(duì)象會(huì)保留其目標(biāo),直到計(jì)時(shí)器本身失效為止,調(diào)用invalidate方法可令計(jì)時(shí)器失效,另外,一次性的計(jì)時(shí)器在觸發(fā)完成任務(wù)之后也會(huì)失效。
  • 反復(fù)執(zhí)行任務(wù)的計(jì)時(shí)器,很容易引起循環(huán)引用,如果這種計(jì)時(shí)器的目標(biāo)對(duì)象有保留了計(jì)時(shí)器本身,肯定會(huì)導(dǎo)致循環(huán)引用,這種循環(huán)引用可能是直接發(fā)生的也可能是通過對(duì)象圖里的其他對(duì)象發(fā)生的。
  • 可以擴(kuò)充NSTimer的其他功能,用塊來打破循環(huán)引用,(目前公共接口已經(jīng)提供,但是該函數(shù)創(chuàng)建的定時(shí)器沒有自動(dòng)加入到runlooph中)

給category添加屬性,使用關(guān)聯(lián)對(duì)象

  • 可以使用objc_setAssocaiatedObject(id object,void *key,id value,objc_AssociationPolicy)該方法以給定的鍵和策略為某對(duì)象設(shè)置關(guān)聯(lián)對(duì)象
  • 定義關(guān)聯(lián)對(duì)象時(shí)可指定內(nèi)存管理語(yǔ)義,用以模仿定義屬性時(shí)所采用的擁有關(guān)系與非擁有關(guān)系
  • 只有在其他做法不可行的時(shí)候才應(yīng)選用關(guān)聯(lián)對(duì)象,因?yàn)檫@種做法通常會(huì)引入難以查找的bug

對(duì)象的isa指針

  • 所有對(duì)象父類的成員變量和自己的成員變量都會(huì)存放在該對(duì)象所對(duì)應(yīng)的存儲(chǔ)空間中
  • 每一個(gè)對(duì)象內(nèi)部都有一個(gè)isa指針,指向他的類對(duì)象,類對(duì)象中存放著本對(duì)象的(對(duì)象方法列表,成員變量列表,屬性列表...),類對(duì)象中也有一個(gè)isa指針指向元對(duì)象(meta class),元對(duì)象的內(nèi)部存放的是類方法列表,類對(duì)象的內(nèi)部還有一個(gè)superclass指針,指向它的父類對(duì)象
  • 根對(duì)象就是NSObject,它的superclass指針指向nil
  • 類對(duì)象既然稱為對(duì)象,那它也是一個(gè)實(shí)例,類對(duì)象中也有一個(gè)isa指針指向他的元類(meta class),即類對(duì)象是元類的實(shí)例。元類內(nèi)部存放的是類方法列表,根元類的isa指針指向的是自己,根元類的superclass指針指向的是NSObject類,如圖:
    [圖片上傳失敗...(image-6564c9-1515825443606)]

objc_msgSend的作用

  • 在Objective-C中,如果向某個(gè)對(duì)象傳遞消息,會(huì)使用動(dòng)態(tài)綁定機(jī)制決定需要調(diào)用的方法,對(duì)象收到消息之后調(diào)用那個(gè)方法完全由運(yùn)行時(shí)期決定的,[someObject messageName:parameter], someObject叫做接收者,messageName叫做選擇子,選擇子和參數(shù)一起稱為消息(message),編譯器看到這個(gè)消息的時(shí)候,將其轉(zhuǎn)換成一條標(biāo)準(zhǔn)的C語(yǔ)言函數(shù)調(diào)用 void objc_msgSend(id self,SEL cmd,...),第一個(gè)參數(shù)是接收者,第二個(gè)參數(shù)是選擇子,后面的參數(shù)是消息中的那些參數(shù)
  • objc_msgSend函數(shù)會(huì)根據(jù)接收者與選擇子的類型來調(diào)用適當(dāng)?shù)姆椒?objc_msgSend函數(shù)會(huì)根據(jù)選擇子去查詢接受者所屬的類中查詢方法列表(methods list),找到就跳轉(zhuǎn)到實(shí)現(xiàn)代碼,并緩存在快速映射表中(每個(gè)類都有快速映射表這樣一塊緩存),如果找不到,沿著繼承體系向上查找,等找到合適的方法之后在跳轉(zhuǎn)。如果最終還是找不到相符的方法,就執(zhí)行消息轉(zhuǎn)發(fā)(messageforwarding)操作。
  • 方法列表(methods list)是一個(gè)映射表,key是選擇子,value是函數(shù)的內(nèi)存地址objc_msgSend函數(shù)正是通過這張表格尋找到應(yīng)該執(zhí)行的方法,并跳轉(zhuǎn)到其實(shí)現(xiàn)的
  • 如果要給父類發(fā)消息,例如[super messageName:parameter],就會(huì)被編譯器轉(zhuǎn)換為objc_msgSendSuper函數(shù)

消息轉(zhuǎn)發(fā)機(jī)制

  • 對(duì)象在收到無法解讀的消息后,首選將調(diào)用所屬類的 +(BOOL)resolveInstanceMethod:(SEL)selector; 這個(gè)方法的參數(shù)就是那個(gè)未知的選擇子。在繼續(xù)執(zhí)行轉(zhuǎn)發(fā)機(jī)制之前這個(gè)類有一個(gè)新增加一個(gè)處理這個(gè)選擇子的方法,如果未實(shí)現(xiàn)的方法不是對(duì)象方法,而是類方法,那么運(yùn)行時(shí)期的系統(tǒng)會(huì)調(diào)用+(BOOL)resolveClassMethod:(SEL)selector,這個(gè)方法和resolveInstanceMethod類似;使用這種方法的前提是:相關(guān)方法的實(shí)現(xiàn)代碼已經(jīng)寫好,只等著運(yùn)行的時(shí)候動(dòng)態(tài)插入帶類里面就可以。
  • 當(dāng)前接收者還有第二次機(jī)會(huì)處理未知的選擇子,運(yùn)行期系統(tǒng)會(huì)問對(duì)象能不能把這條消息轉(zhuǎn)給其他接收者處理,會(huì)調(diào)用對(duì)象的 - (id)forwardingTargetForSelector:(SEL)selector,方法參數(shù)是未知的選擇子,接收者可以找到一個(gè)備援對(duì)象,將其返回,如果返回nil或者self,會(huì)啟用完整的消息轉(zhuǎn)發(fā)。
  • 調(diào)用methodSignatureForSelector:方法,嘗試獲得一個(gè)方法簽名。如果獲取不到,則直接調(diào)用doesNotRecognizeSelector拋出異常。如果能獲取,則返回非nil:創(chuàng)建一個(gè) NSlnvocation 并傳給forwardInvocation:。
  • 完整的消息轉(zhuǎn)發(fā)會(huì)創(chuàng)建一個(gè)NSInvocation對(duì)象,把未處理的那條消息有關(guān)的全部細(xì)節(jié)都封裝于其中,包含選擇子,目標(biāo)對(duì)象,和參數(shù)。傳給目標(biāo)對(duì)象的這個(gè)方法:-(void)forwardInvocation:(NSInvocation *)invocation;這個(gè)方法用來改變調(diào)用目標(biāo),或者不需要處理,但是會(huì)提前創(chuàng)建一個(gè)NSInvocation對(duì)象,開銷更大一些,與備援接收者所處理的方案類似。
  • 如果對(duì)象也沒有實(shí)現(xiàn)-(void)forwardInvocation:(NSInvocation *)invocation;這個(gè)函數(shù),會(huì)在繼承體系中繼續(xù)查找直到NSObject,如果最后調(diào)用了NSobject類的這個(gè)函數(shù),會(huì)調(diào)用NSObject類的- (void)doesNotRecognizeSelector:(SEL)aSelector;這個(gè)函數(shù)拋出一個(gè)異常,表示選擇子沒有能得到有效的處理。
    如圖:
圖片
  • 接收者每一步中均有機(jī)會(huì)處理消息,步驟越往后,處理的消息代價(jià)越大。最好在第一部完成,如果第一次完成,運(yùn)行時(shí)的系統(tǒng)就可以將此方法緩存起來了。如果這個(gè)類的實(shí)例稍后還收到同名的選擇子,就不用在重新啟動(dòng)消息轉(zhuǎn)發(fā)流程。若想在第三步里把消息轉(zhuǎn)給備援的接收者,還不如把轉(zhuǎn)發(fā)操作提前到第二步,應(yīng)為第三步只是修改了調(diào)用目標(biāo),放在第二步會(huì)更簡(jiǎn)單一些,第三步會(huì)創(chuàng)建完整的NSInvocation對(duì)象。

method swizzling 方法交換

  • 在運(yùn)行期,可以向類中新增或者替換選擇子所對(duì)應(yīng)的方法實(shí)現(xiàn)
  • 可以通過方法交換為已經(jīng)有的方法增加新的功能,編寫一個(gè)方法,在此方法中實(shí)現(xiàn)所需要的附加功能,并調(diào)用原有的方法實(shí)現(xiàn)。
  • 一般來說只有調(diào)試程序的時(shí)候才需要在運(yùn)行期修改方法實(shí)現(xiàn),這種方法不宜亂用。

runloop和線程的關(guān)系

  • 總的說來,Run loop,正如其名,loop表示某種循環(huán),和run放在一起就表示一直在運(yùn)行著的循環(huán)。實(shí)際上runloop和線程是緊密相連的,可以這樣說runloop是為了線程而生,沒有線程它就沒有存在的必要。runloop是線程的基礎(chǔ)架構(gòu)部分。Cocoa和CoreFundation都提供了runloop對(duì)象方便配置和管理線程的runloop,每個(gè)線程,包括主線程都有與之對(duì)應(yīng)的runloop對(duì)象。
  • 主線程的runloop是默認(rèn)啟動(dòng)的,iOSAPP啟動(dòng)后會(huì)走UIApplicationMain()函數(shù),這個(gè)函數(shù)會(huì)為mainThread設(shè)置一個(gè)runloop對(duì)象,所以iOSApp可以在無人操作的時(shí)候休息,需要讓它干活的時(shí)候又能立馬響應(yīng)。
  • 對(duì)于其他的線程來說runloop默認(rèn)是沒有啟動(dòng)的,如果需要和用戶交互,可以手動(dòng)配置啟動(dòng)runloop,如果線程只是執(zhí)行一個(gè)長(zhǎng)時(shí)間的已經(jīng)確定的任務(wù),則不需要啟動(dòng)runloop。
  • Cocoa中的NSRunLoop類并不是線程安全的,CoreFundation中的不透明類CFRunLoopRef是線程安全的,兩種類型的runloop可以混合使用.
  • runloop同時(shí)也負(fù)責(zé)autorelease pool的創(chuàng)建和釋放,每當(dāng)一個(gè)運(yùn)行循環(huán)開始的時(shí)候,它會(huì)創(chuàng)建它都會(huì)自動(dòng)創(chuàng)建autorelease pool,每當(dāng)一個(gè)運(yùn)行循環(huán)結(jié)束的時(shí)候它都會(huì)釋放銷毀autorelease pool(autorelease pool銷毀的時(shí)候會(huì)對(duì)autorelease pool中所有的對(duì)象做一次release操作)

runloop的mode作用

作用:線程的運(yùn)行需要不同的模式,去響應(yīng)各種不同的事件,去處理不同情境模式。

  • NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默認(rèn),空閑狀態(tài)
  • UITrackingRunLoopMode:ScrollView滑動(dòng)時(shí)
  • UIInitializationRunLoopMode:?jiǎn)?dòng)時(shí)
  • NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合

蘋果公開提供的 Mode 有兩個(gè):

  • NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
  • NSRunLoopCommonModes(kCFRunLoopCommonModes)

runloop的(input source)和(timer source)

runloop接收時(shí)間來自于兩種不同的源輸入源(input source)和定時(shí)源(timer source),兩種源都使用程序的某一特定的處理例程來處理到達(dá)的事件 如圖:


圖片
  1. 輸入源(input source)
  • 基于端口的輸入源,由內(nèi)核自動(dòng)發(fā)送。Cocoa和Core Foundation內(nèi)置支持使用端口相關(guān)的對(duì)象和函數(shù)來創(chuàng)建的基于端口的源。例如,在Cocoa里面你從來不需要直接創(chuàng)建輸入源。你只要簡(jiǎn)單的創(chuàng)建端口對(duì)象,并使用NSPort的方法把該端口添加到run loop。端口對(duì)象會(huì)自己處理創(chuàng)建和配置輸入源。
    在Core Foundation,你必須人工創(chuàng)建端口和它的run loop源。我們可以使用端口相關(guān)的函數(shù)(CFMachPortRef,CFMessagePortRef,CFSocketRef)來創(chuàng)建合適的對(duì)象。
  • 自定義輸入源,自定義輸入源需要人工從其他的線程發(fā)送。創(chuàng)建自定義輸入源必須使用Core Fundation里面的CFRunLoopSourceRef類型相關(guān)的函數(shù)來創(chuàng)建??梢允褂没卣{(diào)函數(shù)來配置自定義輸入源。Core Fundation會(huì)在配置源的不同地方調(diào)用回調(diào)函數(shù),處理輸入事件,在源從run loop移除的時(shí)候清理它。除了定義在事件到達(dá)時(shí)自定義輸入源的行為,你也必須定義消息傳遞機(jī)制。源的這部分運(yùn)行在單獨(dú)的線程里面,并負(fù)責(zé)在數(shù)據(jù)等待處理的時(shí)候傳遞數(shù)據(jù)給源并通知它處理數(shù)據(jù)。消息傳遞機(jī)制的定義取決于你,但最好不要過于復(fù)雜。
  • Cocoa上的Selector源,Cocoa定義了自定義的輸入源,允許你在任何線程執(zhí)行selector方法,和基于端口的源一樣,執(zhí)行selector請(qǐng)求會(huì)在目標(biāo)線程上序列化,減緩許多在線程上允許多個(gè)方法容易引起的同步問題。不像基于端口的源,一個(gè)selector執(zhí)行完后會(huì)自動(dòng)從run loop里面移除。當(dāng)在其他線程上面執(zhí)行selector時(shí),目標(biāo)線程須有一個(gè)活動(dòng)的run loop。對(duì)于你創(chuàng)建的線程,這意味著線程在你顯式的啟動(dòng)run loop之前是不會(huì)執(zhí)行selector方法的,而是一直處于休眠狀態(tài)。NSObject類提供了類似如下的selector方法:
    • (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)argwaitUntilDone:(BOOL)wait modes:(NSArray *)array;

2 .定時(shí)源(timer source)

  • 定時(shí)源在預(yù)設(shè)的時(shí)間點(diǎn)同步方式傳遞消息,這些消息都會(huì)發(fā)生在特定的時(shí)間或者重復(fù)的時(shí)間間隔,定時(shí)源直接傳遞消息給處理例程,不會(huì)立即退出runloop,需要注意的是,盡管定時(shí)器可以產(chǎn)生基于時(shí)間的通知,但它并不是實(shí)時(shí)機(jī)制。和輸入源一樣,定時(shí)器也和你的run loop的特定模式相關(guān)。如果定時(shí)器所在的模式當(dāng)前未被run loop監(jiān)視,那么定時(shí)器將不會(huì)開始直到run loop運(yùn)行在相應(yīng)的模式下。類似的,如果定時(shí)器在run loop處理某一事件期間開始,定時(shí)器會(huì)一直等待直到下次run loop開始相應(yīng)的處理程序。如果run loop不再運(yùn)行,那定時(shí)器也將永遠(yuǎn)不啟動(dòng)。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容