iOS面試題

KVO 實現(xiàn)原理?

  • 利用 Runtime 動態(tài)生成一個子類,并且讓 instance 對象的 isa 指向這個全新的子類
  • 當修改 instance 對象的屬性時,會調(diào)用 Foundation框架的 _NSSetXXXValueAndNotify 函數(shù) ,該函數(shù)里面會先調(diào)用 willChangeValueForKey: 然后調(diào)用父類原來的 setter 方法修改值,最后是 didChangeValueForKey:。didChangeValueForKey 內(nèi)部會觸發(fā)監(jiān)聽器(Oberser)的監(jiān)聽方法observeValueForKeyPath:ofObject:change:context:
  • 如果需要手動觸發(fā) KVO,只需要手動調(diào)用 willChangeValueForKey:didChangeValueForKey: 兩個方法即可。

KVC 實現(xiàn)原理?

setValue:forKey

  • 按照 setKey:, _setKey: 順序查找方法 ,找到了就傳遞參數(shù),調(diào)用方法。
  • 如果沒有找到,就查看 accessInstanceVariablesDirectly 方法(默認為 YES)的返回值。
  • 如果返回值是 YES,那么按 _key,_isKey,key,iskey 的順序查找成員變量,找到了直接賦值,找不到就調(diào)用 setValue:forUnderfinedKey: 方法并拋出異常NSUnknownKeyException。
  • 如果返回值是 NO,就調(diào)用 setValue:forUnderfinedKey: 方法并拋出異常 NSUnknownKeyException。

valueForKey

  • 按照 getKey,key,isKey, _key 的順序查找方法,找到直接調(diào)用。
  • 如果沒有找到,就查看 accessInstanceVariablesDirectly 方法(默認為 YES)的返回值。
  • 如果返回值是 YES,那么按 _key,_isKey,key,iskey 的順序查找成員變量,找到了直接取值,找不到就調(diào)用 setValue:forUnderfinedKey: 方法并拋出異常NSUnknownKeyException。
  • 如果返回值是 NO,就調(diào)用 setValue:forUnderfinedKey: 方法并拋出異常 NSUnknownKeyException。

消息轉(zhuǎn)發(fā)機制原理?

  • 動態(tài)方法解析:
    對象在接收到未知的消息時,首先會調(diào)用所屬類的類方法 +resolveInstanceMethod: 或者 +resolveClassMethod:。在這個方法中,我們有機會為該未知消息新增一個”處理方法”“。不過使用該方法的前提是我們已經(jīng)實現(xiàn)了該”處理方法”,只需要在運行時通過
    class_addMethod 函數(shù)動態(tài)添加到類里面就可以了。
  • 備用接受者:
    動態(tài)方法解析無法處理消息,則會走備用接受者。這個備用接受者只能是一個新的對象,不能是 self 本身,否則就會出現(xiàn)無限循環(huán)。如果我們沒有指定相應的對象來處理 selector,則應該調(diào)用父類的實現(xiàn)來返回結果。
  • 完整消息轉(zhuǎn)發(fā):
    首先創(chuàng)建 NSInvocation 對象,把尚未處理的消息有關的內(nèi)容封于其中,此對象包括 selector、目標(target 及參數(shù)。在觸發(fā) NSInvocation 對象時,”消息派發(fā)系統(tǒng)“(message-dispatch sys)將會把消息指派給目標對象。若發(fā)現(xiàn)某個操作不應該是本類來處理,就需要調(diào)用父類的同名方法。這樣,繼承體系中的每個類都有機會處理此調(diào)用請求,直至 NSObject。如果最后調(diào)用了 NSObject 的方法,最終該方法就會繼續(xù)調(diào)用 doesNotRecognizeSelector: 拋出異常,表明 'selector' 未能被處理。

理解 weak 屬性?

  • Runtime 維護了一個 weak 表,用于存儲指向某個對象的所有 weak 指針。weak 表其實是一個 hash(哈希)表,Key 是所指對象的地址,Valueweak 指針的地址(這個地址的值是所指對象的地址)數(shù)組。
  • 初始化時:runtime 會調(diào)用 objc_initWeak 函數(shù),初始化一個新的 weak 指針指向?qū)ο蟮牡刂贰?/li>
  • 添加引用時:objc_initWeak 函數(shù)會調(diào)用 objc_storeWeak() 函數(shù),objc_storeWeak() 的作用是更新指針指向,創(chuàng)建對應的弱引用表。
  • 釋放時,調(diào)用 clearDeallocating 函數(shù)。clearDeallocating 函數(shù)首先根據(jù)對象地址獲取所有 weak 指針地址的數(shù)組,然后遍歷這個數(shù)組把其中的數(shù)據(jù)設為 nil,最后從 weak 表中刪除并清理對象的記錄。

項目中網(wǎng)絡層如何做安全處理?

  • 盡量使用 https
    https 可以過濾掉大部分的安全問題。https 在證書申請,服務器配置,性能優(yōu)化,客戶端配置上都需要投入精力,所以缺乏安全意識的開發(fā)人員容易跳過 https,或者拖到以后遇到問題再優(yōu)化。https 除了性能優(yōu)化麻煩一些以外其他都比想象中的簡單,如果沒精力優(yōu)化性能,至少在注冊登錄模塊需要啟用 https,這部分業(yè)務對性能要求比較低。
  • 不要傳輸明文密碼:
    不知道現(xiàn)在還有多少 app 后臺是明文存儲密碼的。無論客戶端,server 還是網(wǎng)絡傳輸都要避免明文密碼,要使用 hash 值。客戶端不要做任何密碼相關的存儲,hash 值也不行。存儲 token 進行下一次的認證,而且 token 需要設置有效期,使用 refreshtoken 去申請新的 token
  • post 并不比 get 安全:
    事實上,postget 一樣不安全,都是明文。參數(shù)放在 queryString 或者 body 沒任何安全上的差別。在 http 的環(huán)境下,使用 post 或者 get 都需要做加密和簽名處理。
  • 不要使用 301 跳轉(zhuǎn):
    301 跳轉(zhuǎn)很容易被 http 劫持攻擊。移動端 http 使用 301 比桌面端更危險,用戶看不到瀏覽器地址,無法察覺到被重定向到了其他地址。如果一定要使用,確保跳轉(zhuǎn)發(fā)生在 https 的環(huán)境下,而且 https 做了證書綁定校驗。
  • http 請求都帶上 MAC:
    所有客戶端發(fā)出的請求,無論是查詢還是寫操作,都帶上MAC(Message AuthenticationCode)。MAC 不但能保證請求沒有被篡改(Integrity),還能保證請求確實來自你的合法客戶端(Signing)。當然前提是你客戶端的 key 沒有被泄漏,如何保證客戶端 key 的安全是另一個話題。MAC 值的計算可以簡單的處理為 hash(request params+key)。帶上 MAC 之后,服務器就可以過濾掉絕大部分的非法請求。MAC 雖然帶有簽名的功能,和 RSA 證書的電子簽名方式卻不一樣,原因是 MAC 簽名和簽名驗證使用的是同一個 key,而 RSA 是使用私鑰簽名,公鑰驗證,MAC 的簽名并不具備法律效應。
  • http 請求使用臨時密鑰:
    高延遲的網(wǎng)絡環(huán)境下,不經(jīng)優(yōu)化 https 的體驗確實會明顯不如 http。在不具備 https 條件或?qū)W(wǎng)絡性能要求較高且缺乏 https 優(yōu)化經(jīng)驗的場景下,http 的流量也應該使用 AES 進行加密。AES 的密鑰可以由客戶端來臨時生成,不過這個臨時的 AESkey 需要使用服務器的公鑰進行加密,確保只有自己的服務器才能解開這個請求的信息,當然服務器的 response 也需要使用同樣的 AESkey 進行加密。由于 http 的應用場景都是由客戶端發(fā)起,服務器響應,所以這種由客戶端單方生成密鑰的方式可以一定程度上便捷的保證通信安全。
  • AES 使用 CBC 模式:
    不要使用 ECB 模式,記得設置初始化向量,每個 block 加密之前要和上個 block 的密文進行運算。

main() 之前的過程有哪些?

  • dyld 開始將程序二進制文件初始化
  • 交由 ImageLoader 讀取 image,其中包含了我們的類,方法等各種符號(Class、Protocol 、Selector、 IMP)
  • 由于 runtimedyld 綁定了回調(diào),當 image 加載到內(nèi)存后,dyld 會通知 runtime 進行處理
  • runtime 接手后調(diào)用 map_images 做解析和處理
  • 接下來 load_images 中調(diào)用 call_load_methods 方法,遍歷所有加載進來的 Class,按繼承層次依次調(diào)用 Class+load 和其他 Category+ load 方法
  • 至此 所有的信息都被加載到內(nèi)存中
  • 最后 dyld 調(diào)用真正的 main 函數(shù)
  • dyld 會緩存上一次把信息加載內(nèi)存的緩存,所以第二次比第一次啟動快

為什么說 Objective-C 是一門動態(tài)的語言?

  • Objective-C 是 C 語言的一個子類,所以 Objective-C 是一個靜態(tài)語言,但 Objective-C 的三大特性之一的多態(tài)讓其擁有了動態(tài)性。
  • Objective-C 的動態(tài)性,讓程序在運行時判斷其該有的行為,而不是像 C 等靜態(tài)語言在編譯構建時就確定下來。它的動態(tài)性主要體現(xiàn)在 3 個方面:
    1.動態(tài)類型:如 id 類型。實際上靜態(tài)類型因為其固定性和可預知性而使用的特別廣泛。靜態(tài)類型是強類型,動態(tài)類型是弱類型,運行時決定接收者。
    2.動態(tài)綁定:讓代碼在運行時判斷需要調(diào)用什么方法,而不是在編譯時。與其他面向?qū)ο笳Z言一樣,方法調(diào)用和代碼并沒有在編譯時連接在一起,而是在消息發(fā)送時才進行連接。運行時決定調(diào)用哪個方法。
    3.動態(tài)載入。讓程序在運行時添加代碼模塊以及其他資源。用戶可以根據(jù)需要執(zhí)行一些可執(zhí)行代碼和資源,而不是在啟動時就加載所有組件。可執(zhí)行代碼中可以含有和程序運行時整合的新類。

MVC 和 MVVM,MVP ?

MVC

  • view 傳送指令到 controller
  • controller 完成業(yè)務邏輯后,要求 model 改變狀態(tài)
  • model 將新的數(shù)據(jù)發(fā)送到 view,用戶得到反饋
  • 所有通信都是單向的

MVP

  • controller 改名為 presenter,同時改變了通信方向
  • 各部分之間的通信,都是雙向的
  • viewmodel 不發(fā)生聯(lián)系,都通過 presenter 傳遞
  • view 不部署任何業(yè)務邏輯,稱為"被動視圖"(Passive View),即沒有任何主動性,而 presenter 部署所有邏輯

MVVM

  • presenter 改名為 viewModel,基本上與 MVP 模式完全一致
  • MVVM 采用雙向綁定(data-binding):view 的變動,自動反映在 viewModel,反之亦然

為什么代理要用 weak?代理的 delegate 和 dataSource 有什么區(qū)別? block 和代理的區(qū)別?

  • 代理使用 weak 來修飾:
    1.為了避免循環(huán)引用。
    2.當對象釋放的時候,系統(tǒng)會對屬性賦值 nil,Objective-C 有個特性就是對 nil 對象發(fā)送消息也就是調(diào)用方法,不會 cash。
  • delegate:傳遞的是事件(even)
  • dataSource:傳遞的是數(shù)據(jù)
  • block 和代理的區(qū)別
    1.代理更面向過程,block 更面向結果

屬性的實質(zhì)是什么?包括哪幾個部分?屬性默認的關鍵字都有哪些?@dynamic 關鍵字和 @synthesize 關鍵字是用來做什么的?

  • @property = ivar + getter + setter
  • 基本數(shù)據(jù) atomic, readwrite, assign
  • 對象 atomic, readwrite, strong
  • @dynamic 修飾的屬性,其 gettersetter 方法編譯器是不會自動幫你生成,必須自己是實現(xiàn)的。
  • @synthesize 修飾的屬性,其 gettersetter 方法編譯器是會自動幫你生成,不必自己實現(xiàn),且指定與屬性相對應的成員變量。

atomic 安全么?

  • atomic 原子操作,所謂原子,就是不可再化分,已經(jīng)是最小的操作單位(所謂操作指的是對內(nèi)存的讀寫)
  • 一個數(shù)據(jù)的線程安全,簡單點來說就是這塊數(shù)據(jù)即使有多個線程同時讀寫,也不會出現(xiàn)數(shù)據(jù)的錯亂,內(nèi)存的最后狀態(tài)是可預見的
  • 在 64 位的操作系統(tǒng)下,所有類型的指針,包括 void * 都是占用 8 個字節(jié),以 Objective-C 下的 NSArray * 為例子,如果一個多線程操作這個數(shù)據(jù),會有兩個層級的并發(fā)問題,1、指針本身。2、指針所指向的內(nèi)存,所以這個數(shù)據(jù) array 多線程操作的時候,必須分成兩部分來描述,一個是 &array 這個指針本身,另一個則是它所指向的內(nèi)存 array
  • @property(atomic)NSArray *array 其實修飾的是這個指針,也就是這個 8 字節(jié)內(nèi)存,跟第二部分數(shù)據(jù) n 字節(jié)沒有任何關系,被 atomic 修飾之后,你不可能隨意去多線程操作這個 8 字節(jié),但是對 8 字節(jié)里面所指向的 n 字節(jié)沒有任何限制
  • atomic 只對 getset 方法起作用:我們知道,這個 8 字節(jié)里面存儲的數(shù)據(jù),是 n 字節(jié)數(shù)據(jù)的頭地址,如果更改 8 字節(jié)數(shù)據(jù)的內(nèi)容,那么最后通過這個指針訪問到的數(shù)據(jù)就會完全不一樣

如何令自己所寫的對象具有拷貝功能?

  • 遵循 NSCopying 協(xié)議,并且實現(xiàn) - (id)copyWithZone:(NSZone *)zone 方法
  • 如果讓自己的類具備 mutableCopy 方法,必須遵守 NSMutableCopying,并實現(xiàn) - (id)mutableCopyWithZone:(nullable NSZone *)zone 方法

進程和線程的區(qū)別?同步異步的區(qū)別?并行和并發(fā)的區(qū)別?

進程和線程的區(qū)別

  • 進程是資源的分配和調(diào)度的一個獨立單元,而線程是 CPU 調(diào)度的基本單元
  • 同一個進程中可以包括多個線程,并且線程共享整個進程的資源(寄存器、堆棧、上下文),一個進程至少包括一個線程。
  • 進程的創(chuàng)建調(diào)用 fork 或者 vfork,而線程的創(chuàng)建調(diào)用 pthread_create,進程結束后它擁有的所有線程都將銷毀,而線程的結束不會影響同個進程中的其他線程的結束
  • 線程是輕兩級的進程,它的創(chuàng)建和銷毀所需要的時間比進程小很多,所有操作系統(tǒng)中的執(zhí)行功能都是創(chuàng)建線程去完成的
  • 線程中執(zhí)行時一般都要進行同步和互斥,因為他們共享同一進程的所有資源
  • 線程有自己的私有屬性 TCB,線程 id,寄存器、硬件上下文,而進程也有自己的私有屬性進程控制塊 PCB,這些私有屬性是不被共享的,用來標示一個進程或一個線程的標志

同步和異步的區(qū)別

  • 同步(synchronous):進程之間的關系不是相互排斥臨界資源的關系,而是相互依賴的關系。進一步的說明:就是前一個進程的輸出作為后一個進程的輸入,當?shù)谝粋€進程沒有輸出時第二個進程必須等待。具有同步關系的一組并發(fā)進程相互發(fā)送的信息稱為消息或事件
  • 異步(asynchronous):異步和同步是相對的,同步就是順序執(zhí)行,執(zhí)行完一個再執(zhí)行下一個,需要等待、協(xié)調(diào)運行。異步就是彼此獨立,在等待某事件的過程中繼續(xù)做自己的事,不需要等待這一事件完成后再工作。線程就是實現(xiàn)異步的一個方式。異步是讓調(diào)用方法的主線程不需要同步等待另一線程的完成,從而可以讓主線程干其它的事情

并行和并發(fā)的區(qū)別

  • 并發(fā):在操作系統(tǒng)中,是指一個時間段中有幾個程序都處于已啟動運行到運行完畢之間,且這個幾個程序都是在同一個處理機上運行。其中兩種并發(fā)關系分別是同步和互斥。
    互斥:進程間相互排斥的使用臨界資源的現(xiàn)象
    同步:進程之間的關系不是相互排斥臨界資源的關系,而是相互依賴的關系。進一步說明就是前一個進程的輸出作為后一個進程的輸入,當?shù)谝粋€進程沒有輸出時第二個進程必須等待。具有同步關系的一組并發(fā)進程相互發(fā)送的信息稱為消息或事件。
    其中并發(fā)又有偽并發(fā)和真并發(fā),偽并發(fā)是指單核處理器的并發(fā),真并發(fā)是指多核處理器的并發(fā)。
  • 并行(parallelism):在單處理器中多道程序設計系統(tǒng)中,進程被交替執(zhí)行,表現(xiàn)出一種并發(fā)的外部特種;在多處理器系統(tǒng)中,進程不僅可以交替執(zhí)行,而且可以重疊執(zhí)行。在多處理器上的程序才可實現(xiàn)并行處理。從而可知,并行是針對多處理器而言的。并行是同時發(fā)生的多個并發(fā)事件,具有并發(fā)的含義,但并發(fā)不一定并行,也亦是說并發(fā)事件之間不一定要同一時刻發(fā)生。

Designated Initializer ?

  • 指定初始化函數(shù)對一個類來說非常重要,通常參數(shù)也是最多的,試想每次我們需要創(chuàng)建一個自定義類都需要一堆參數(shù),那豈不是很痛苦。便利初始化函數(shù)就是用來幫我們解決這個問題的,可以讓我們比較的創(chuàng)建對象,同時又可以保證類的成員變量被設置為默認的值。
  • 子類如果有指定初始化函數(shù),那么指定初始化函數(shù)實現(xiàn)時必須調(diào)用它的直接父類的指定初始化函數(shù)
  • 如果子類有指定初始化函數(shù),那么便利初始化函數(shù)必須調(diào)用自己的其它初始化函數(shù)(包括指定初始化函數(shù)以及其他的便利初始化函數(shù)),不能調(diào)用 super 的初始化函數(shù)

能否向編譯后得到的類中增加實例變量?能否向運行時創(chuàng)建的類中添加實例變量?

  • 不能向編譯后得到的類增加實例變量,編譯后的類已經(jīng)注冊在 runtime 中,類結構體中的 objc_ivar_list 實例變量的鏈表和 instance_size 實例變量的內(nèi)存大小已經(jīng)確定, runtime 會調(diào)用 class_setvarlayoutclass_setWeaklvarLayout 來處理 strong weak 引用,所以不能向存在的類中添加實例變量
  • 能向運行時創(chuàng)建的類中添加實例變量,運行時創(chuàng)建的類是可以添加實例變量,調(diào)用 class_addIvar 函數(shù)。但是需要在調(diào)用 objc_allocateClassPair 之后, objc_registerClassPair 之前,原因同上

給類添加一個屬性后,在類結構體里哪些元素會發(fā)生變化?

  • instance_size: 實例的內(nèi)存大小
  • objc_ivar_list *ivars: 屬性列表

runloop 的 mode 是用來做什么的?有幾種 mode?

  • model 是 runloop 里面的模式,不同的模式下的 runloop 處理的事件和消息有一定的差別,系統(tǒng)默認注冊了 5 個 model 。
  • kCFRunLoopDefaultMode: App 的默認 Mode,通常主線程是在這個 Mode 下運行的。
  • UITrackingRunLoopMode: 界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響。
  • UIInitializationRunLoopMode: 在剛啟動 App 時第進入的第一個 Mode,啟動完成后就不再使用。
  • GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到。
  • kCFRunLoopCommonModes: 這是一個占位的 Mode,沒有實際作用。
  • 5 種 model 進行了封裝
    NSDefaultRunLoopMode
    NSRunLoopCommonModes
  • NStime 對象默認是在 NSDefaultRunLoopMode 下面調(diào)用消息的,但是當我們滑動 scrollview 的時候,NSDefaultRunLoopMode 模式就自動切換到UITrackingRunLoopMode 模式下面

isa 指針?

  • 對象的 isa 指針指向所屬的類
  • 類的 isa 指針指向了所屬的元類(metaclass)
  • 元類的 isa 指向了根元類(root metaclass),根元類本身的 isa 指針指向自己,這樣就形成了一個閉環(huán)

Objective-C 中向一個 nil 對象發(fā)送消息將會發(fā)生什么?

  • Objective-C 中向 nil 發(fā)送消息是完全有效的,只是在運行時不會有任何作用。
  • 如果一個方法返回值是一個對象,那么發(fā)送給 nil 的消息將返回 0 (nil)。
  • 如果方法返回值為指針類型,其指針大小為小于或者等于 sizeof(void*),float,double,long double 或者 long long 的整型標量,發(fā)送給 nil 的消息將返回 0。
  • 如果方法返回值為結構體,發(fā)送給 nil 的消息將返回 0。結構體中各個字段的值將都是 0。其他的結構體數(shù)據(jù)類型將不是用 0 填充的。
  • 如果方法的返回值不是上述提到的幾種情況,那么發(fā)送給nil的消息的返回值將是未定義的。

.a 與 .framework 庫的區(qū)別?靜態(tài)庫和動態(tài)庫的區(qū)別?

  • .a 是一個純二進制文件,不能直接使用,至少要有 .h 文件配合。
  • .framework 中除了有二進制文件之外還有資源文件,可以直接使用。
  • 靜態(tài)庫:以 .a 和 .framework 為文件后綴名,鏈接時會被完整的復制到可執(zhí)行文件中,被多次使用就有多份拷貝。
  • 動態(tài)庫:以.tbd (之前叫 .dylib) 和 .framework 為文件后綴名,鏈接時不復制,程序運行時由系統(tǒng)動態(tài)加載到內(nèi)存,系統(tǒng)只加載一次,多個程序共用(如系統(tǒng)的 UIKit.framework 等),節(jié)省內(nèi)存。
  • Apple 不讓使用自己的動態(tài)庫,iOS8 之后雖然可以上傳含有動態(tài)庫的 App,但是 Apple 不僅需要你動態(tài)庫和 App 的簽名一致,而且蘋果會在你上架的時候再經(jīng)過一次 AppStore 的簽名。

如何讓靜態(tài)庫中的 Category 變得可用?

  • Objective-C 不會為方法定義 linker symbols,它只會為每一個類定義 linker symbols。如果你使用 category 擴展了一個已經(jīng)存在的類,那么 linker 不會將已有類的實現(xiàn)跟 category 的實現(xiàn)連接起來,這就導致了調(diào)用靜態(tài)庫中 category 中新增加的方法時拋出 selector not recognized 的異常。
  • 通過在 Other Linker Flags 添加 -all_load ,它會告訴編譯器“對于所有文檔中的所有對象文件,不管里面的符號有沒有被用到,都載入”,這種方法確實可以,但是會產(chǎn)生比較大的二進制文件。
  • 添加 -force_load 和指定的路徑,這種方法和 -all_load 很像,不同的是它只使用指定的歸檔。
  • Other Linker Flags 中添加-ObjC,這個標識告訴編譯器“如果你在文檔里的對象文件中發(fā)現(xiàn)了 Objective-C 代碼,就把它載入“,Category 里當然也有 Objective-C 代碼。使用這種方法不會載入任何沒有 Objective-C 代碼的文件
  • 啟用 Xcode 里 build setting 中的 PerformSingle-Object PreLink,所有的對象文件都會被合并成一個單文件(這不是真正的鏈接,所以叫做預鏈接),這個對象文件(有時被稱做主對象文件(masterobject file)被添加到文檔中?,F(xiàn)在如果主對象文件中的任何符號被認為是“在使用”,整個主對象文件都會被認為在使用,這樣它里面的 Objective-C 部分就會被載入了。因為里面的類都被正常符號化了,所以能使從這樣的靜態(tài)庫中使用所有的 category。
  • 在只有 category 的源文件里添加 Fakesymbol。如果你想在 runtime 里使用 category,一定要確保你以某種方法在編譯時引用了 fake symbol,這會使得對象文件以及它里面的 Objective-C 代碼被載入。和上面其他的解決方法不一樣,這種解決方法可以控制哪些 category 可以在 runtime 里被編譯后的代碼使用(可以通過使用這個符號,使它們被鏈接并變得可用;也可以不使用這個符號,這樣鏈接器就會忽略它)。

BitCode 的理解。

  • Bitcode 是被編譯程序的一種中間形式的代碼。包含 Bitcode 配置的程序?qū)?App store 上被編譯(可執(zhí)行的 64 位或 32 位程序)和鏈接。
  • Bitcode,做的事情是指令集優(yōu)化,根據(jù)你設備的狀態(tài)去做編譯優(yōu)化,進而提升性能,對包的大小優(yōu)化起不到什么本質(zhì)上的作用。
  • APP Thining 是由 App Slicing、On Demand Resources和 Bitcode 組成。
  • App Slicing:根據(jù)你設備型號,生成對應資源的 ipa,以節(jié)省空間。
  • On Demand Resources:按需加載資源

WKWebView 和 UIWebView 的區(qū)別?無痕瀏覽的實現(xiàn)。

  • 在性能、穩(wěn)定性、功能方面有很大提升,直觀體現(xiàn)是內(nèi)存占用變少。
  • 允許 JavaScriptNitro 庫加載并使用(UIWebView 中限制)。
  • 支持了更多的 HTML5 特性。
  • 高達 60fps 的滾動刷新率以及內(nèi)置手勢。
  • UIWebViewDelegateUIWebView 重構成了14類與3個協(xié)議(詳見官方文檔)。
  • WKWebView的 Cookie 存儲在 WKWebsiteDataStore 中。WKWebsiteDataStore 中存儲了包括 cookies、disk、memory caches、WebSQL、IndexedDB 數(shù)據(jù)庫和本地存儲等 web 內(nèi)容。
//defaultDataStore 是默認選擇的存儲容器
+ (WKWebsiteDataStore *)defaultDataStore;
//nonPersistentDataStore 會禁止任何數(shù)據(jù)寫入文件系統(tǒng),可用于無痕瀏覽
+ (WKWebsiteDataStore *)nonPersistentDataStore;
//可以查看到容器中存儲的網(wǎng)站數(shù)據(jù)的所有種類
+ (NSSet<NSString *> *)allWebsiteDataTypes;
//獲取容器中的數(shù)據(jù)記錄
- (void)fetchDataRecordsOfTypes:(NSSet<NSString *> *)dataTypes completionHandler:(void (^)(NSArray<WKWebsiteDataRecord *> *))completionHandler;
//刪除容器中的數(shù)據(jù)記錄
- (void)removeDataOfTypes:(NSSet<NSString *> *)dataTypes forDataRecords:(NSArray<WKWebsiteDataRecord *> *)dataRecords completionHandler:(void (^)(void))completionHandler;
- (void)removeDataOfTypes:(NSSet<NSString *> *)websiteDataTypes modifiedSince:(NSDate *)date completionHandler:(void (^)(void))completionHandler;

NSURLSession 和 NSURLConnection 的區(qū)別?

  • NSURLConnection 是 iOS2.0 后推出的,NSURLSession 是 iOS7.0 后推出的,用于代替 NSURLConnection。
  • 下載任務方式:NSURLConnection 下載文件時,先是將整個文件下載到內(nèi)存,然后再寫入到沙盒,如果文件比較大,就會出現(xiàn)內(nèi)存暴漲的情況。而使用 NSURLSessionUploadTask 下載文件,會默認下載到沙盒中的 tem 文件中,不會出現(xiàn)內(nèi)存暴漲的情況,但是在下載完成后會把 tem 中的臨時文件刪除,需要在初始化任務方法時,在 completionHandler 回調(diào)中增加保存文件的代碼。
  • 請求方式的控制:NSURLConnection 實例化對象,實例化開始,默認請求就發(fā)送(同步發(fā)送),不需要調(diào)用 start 方法。而 cancel 可以停止請求的發(fā)送,停止后不能繼續(xù)訪問,需要創(chuàng)建新的請求。NSURLSession 有三個控制方法,取消(cancel)、暫停(suspend)、繼續(xù)(resume),暫停以后可以通過繼續(xù)恢復當前的請求任務。
  • 斷點續(xù)傳實現(xiàn)方式 :·NSURLConnection· 進行斷點下載,通過設置訪問請求的 HTTPHeaderFieldRange 屬性,開啟運行循環(huán),NSURLConnection 的代理方法作為運行循環(huán)的事件源,接收到下載數(shù)據(jù)時代理方法就會持續(xù)調(diào)用,并使用 NSOutputStream 管道流進行數(shù)據(jù)保存。NSURLSession 進行斷點下載,當暫停下載任務后,如果 downloadTask(下載任務)為非空,調(diào)用 cancelByProducingResumeData:(void (^)(NSData *resumeData))completionHandler 這個方法,這個方法接收一個參數(shù),完成處理代碼塊,這個代碼塊有一個 NSData 參數(shù) resumeData,如果 resumeData 非空,我們就保存這個對象到視圖控制器的 resumeData 屬性中,在點擊再次下載時,通過調(diào)用 [ [self.session downloadTaskWithResumeData:self.resumeData] resume] 方法進行繼續(xù)下載操作,使用 NSURLSession 進行斷點下載更加便捷.
  • 配置信息:NSURLSession 的構造方法 (sessionWithConfiguration:delegate:delegateQueue) 中有一個 NSURLSessionConfiguration 類的參數(shù)可以設置配置信息,其決定了 cookie,安全和高速緩存策略,最大主機連接數(shù),資源管理,網(wǎng)絡超時等配置。NSURLConnection 不能進行這個配置,相比較與 NSURLConnection 依賴與一個全局的配置對象,缺乏靈活性而言,NSURLSession 有很大改進。
  • NSURLSession 支持 HTTP 2.0

WKWebView 支持 NSURLProtocol 嗎

  • WKWebView 是支持 NSURLProtocol 攔截的,只是 WebKit.framework 還不完善。

WebKit 源碼由三大部分組成:

  • WebCore:HTML 排版引擎核心,主要包含 Loader,Parser(DOM,Render),Layout,Paint 等模塊
  • WebKit:移植層,主要包括 GUI,F(xiàn)ile System,Thread,圖片解碼等與平臺相關的模塊
  • JavaScriptCore:JS 虛擬機,主要用于操作 DOM,解析執(zhí)行 JavaScript 代碼
    WebKit
  • WKWebView:網(wǎng)頁的渲染與展示,通過 WKWebViewConfiguration 可以進行配置。
  • WKWebViewConfiguration:這個類專門用來配置 WKWebView。
  • WKPreference:這個類用來進行相關設置。
  • WKProcessPool:這個類用來配置進程池,與網(wǎng)頁視圖的資源共享有關。
  • WKUserContentController:這個類主要用來做 nativeJavaScript 的交互管理。
  • WKUserScript:用于進行 JavaScript 注入。
  • WKScriptMessageHandler:這個類專門用來處理 JavaScript 調(diào)用 native 的方法。
  • WKNavigationDelegate:網(wǎng)頁跳轉(zhuǎn)間的導航管理協(xié)議,這個協(xié)議可以監(jiān)聽網(wǎng)頁的活動。
  • WKNavigationAction:網(wǎng)頁某個活動的示例化對象。
  • WKUIDelegate:用于交互處理 JavaScript 中的一些彈出框。
  • WKBackForwardList:堆棧管理的網(wǎng)頁列表。
  • WKBackForwardListItem:每個網(wǎng)頁節(jié)點對象。

WKWebView 在 WebKit 中的初始化流程:

  • 根據(jù)配置項 WKWebViewConfiguration 創(chuàng)建新 WKWebView,同時會初始化 WKScrollViewWKContentView;
  • WKContentView 從進程池 WKProcessPool 中分配 WebProcessProxyWebPageProxy,同時根據(jù)當前 Page 初始化 WKBrowsingContextController,提供了大部分交互操作功能;
  • WKWebView 在獨立于 App Process 進程之外的 Network Process 進程中執(zhí)行網(wǎng)絡請求,請求數(shù)據(jù)不經(jīng)過主進程,因此,在 WKWebView 上直接使用 NSURLProtocol 無法攔截請求。

一個完整的網(wǎng)絡請求代理攔截處理流程:

  • WKBrowsingContextController 通過 registerSchemeForCustomProtocolWebProcessPool 注冊全局自定義 scheme
  • WebProcessPool 使用已注冊的 scheme 初始化 Network Process 進程配置,同時設置 CustomProtocolManager,負責把網(wǎng)絡請求通過 IPC 發(fā)送到 App Process 進程、也接收從 App Process 進程返回的網(wǎng)絡響應 response
  • CustomProtocolManager 注冊了 NSURLProtocol 的子類 WKCustomProtocol,負責攔截網(wǎng)絡請求處理
  • CustomProtocolManagerProxy 中的 WKCustomProtocolLoader 使用 NSURLConnection 發(fā)送實際的網(wǎng)絡請求,并將響應 response 返回給 CustomProtocolManager
  • 詳見NSURLProtocol-WebKitSupport

__weak & __unsafe_unretained 的區(qū)別

  • __unsafe_unretained: 不會對對象進行 retain,當對象銷毀時,依然會指向之前的內(nèi)存空間(野指針)。
  • __weak:不會對對象進行 retain,當對象銷毀時,會自動置為 nil。

NSCatch 和 NSDictionary區(qū)別?

  • NSCache 在系統(tǒng)內(nèi)存很低時會自動釋放對象。
  • NSCache 是線程安全的,在進行多線程操作時,不需要進行加鎖。
  • NSCatch 可以給對象設置上限,用以限制緩存中的對象總個數(shù)。
  • NSCache 不會拷貝 Key。

實例方法與類方法的區(qū)別

實例方法

  • 減號 - 開頭。
  • 只能由對象來調(diào)用。
  • 對象方法中能訪問當前對象的成員變量(實例變量)。
  • self 是對象的首地址。

類方法

  • 加號 + 開頭。
  • 只能由類(名)來調(diào)用。
  • 類方法中不能訪問成員變量(實例變量)。
  • selfClass
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 多線程、特別是NSOperation 和 GCD 的內(nèi)部原理。運行時機制的原理和運用場景。SDWebImage的原...
    LZM輪回閱讀 2,116評論 0 12
  • 最全的iOS面試題及答案 iOS面試小貼士 ———————————————回答好下面的足夠了-----------...
    zweic閱讀 2,801評論 0 73
  • 史上最全的iOS面試題及答案 iOS面試小貼士———————————————回答好下面的足夠了----------...
    Style_偉閱讀 2,567評論 0 35
  • iOS面試小貼士 ———————————————回答好下面的足夠了------------------------...
    不言不愛閱讀 2,242評論 0 7
  • "黃山好看,還是泰山好看?" "黃山美!" 當在纜車上偶遇一位山東游客時,他告訴我黃山美過泰山。 為什么登黃山會想...
    二馬行空閱讀 413評論 0 2

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