丁香園iOS電話面試問題總結

W_C__L發表於2017-06-21

今天下午去面試去面試一家初創公司,然後又接到到了丁香園的電話面試,這篇blog記錄一下面試的一些問題,有的回答的還行,有點感覺不太好,主要是有些英文單詞說的太low了估計被鄙視了吧,下面給大家總結一下面試的一些問題,有些回答是摘要一些大神blog的出處,都有給原連結,希望見諒~~

簡單講解一下http請求,以及GET POST的區別

這個問得其實不是很難,主要看你了不瞭解了,我因為了解一些後臺的東西,所以回答的還行,下面我給大家看兩幅圖片大家就基本瞭解了:

  • 請求Request的原資料

丁香園iOS電話面試問題總結

  • 返回Resonse的原資料

丁香園iOS電話面試問題總結

總結一下,其實http請求就是傳送和接受報文,報文的具體格式就如上圖所示,具體由三部分構成,GET 和 POST比較明顯的不同就是請求方式和引數的位置不同,其他原理的不同大家可以去下面的連結去看:

  • 請求的方法URL協議/版本
  • 請求頭(Request Header)
  • 請求正文(Content)

其中請求頭裡面可以放很多引數,比如報文的大小啊,啥的一些引數,具體可以百度,這裡就不展開了。

下面給兩個大神們推薦的連結,大家可以自行檢視:

https的加密方式和幾次握手

這個問題回答的就比較菜了,程式比較複製,當時特意記了一下,沒想到面試的時候還是有點蒙,回答的非常菜,現在在普及一下,下面是一篇講的很詳細的blog地址,大家可以去原地址去看,我下面也簡單總結一下:

https的加密方式

  • 對稱加密

​ 對稱加密是指加密和解密使用相同金鑰的加密演算法。它要求傳送方和接收方在安全通訊之前,商定一個金鑰。對稱演算法的安全性依賴於金鑰,洩漏金鑰就意味著任何人都可以對他們傳送或接收的訊息解密,所以金鑰的保密性對通訊至關重要。

對稱加密演算法的優、缺點:

優點:演算法公開、計算量小、加密速度快、加密效率高。

缺點:
1)交易雙方都使用同樣鑰匙,安全性得不到保證;

2)每對使用者每次使用對稱加密演算法時,都需要使用其他人不知道的惟一鑰匙,這會使得發收信雙方所擁有的鑰匙數量呈幾何級數增長,金鑰管理成為使用者的負擔。

3)能提供機密性,但是不能提供驗證和不可否認性。

  • 非對稱加密

​ 這種加密或許理解起來比較困難,這種加密指的是可以生成公鑰和私鑰。凡是公鑰加密的資料,公鑰自身不能解密,而需要私鑰才能解密;凡是私鑰加密的資料,私鑰不能解密,需要公鑰才能解密。這種演算法事實上有很多,常用的是RSA,其基於的數學原理是兩個大素數的乘積很容易算,而拿到這個乘積去算出是哪兩個素數相乘就很複雜了,具體原理有興趣可以自行研究。

非對稱加密相比對稱加密更加安全,但也存在兩個明顯缺點:

​ 1)CPU計算資源消耗非常大。一次完全TLS握手,金鑰交換時的非對稱解密計算量佔整個握手過程的90%以上。而對稱加密的計算量只相當於非對稱加密的0.1%,如果應用層資料也使用非對稱加解密,效能開銷太大,無法承受。

​ 2)非對稱加密演算法對加密內容的長度有限制,不能超過公鑰長度。比如現在常用的公鑰長度是2048位,意味著待加密內容不能超過256個位元組。

所以公鑰加密目前只能用來作金鑰交換或者內容簽名,不適合用來做應用層傳輸內容的加解密。

加密的詳細過程

​ 首先伺服器端用非對稱加密(RSA)產生公鑰和私鑰。然後把公鑰發給客 戶端,路徑或許有人會擷取,但是沒有用,因為用公鑰加密的檔案只有私鑰可以解密,而私鑰永遠都不會離開伺服器的。當公鑰到達客戶端之後,客戶端會用對稱加密產生一個祕鑰並且用公鑰來加密傳送給伺服器端,這個祕鑰就是以後用來通訊的鑰匙。這樣伺服器端收到公鑰加密的祕鑰時就可以用私鑰來解公鑰從而獲得祕鑰。這樣的話客戶端和伺服器端都獲得了祕鑰,資訊交流相對是安全的。流程圖如下:

丁香園iOS電話面試問題總結

​ 聽起來確實是挺安全的,但實際上,還有一種更惡劣的攻擊是這種方法無 法防範的,這就是傳說中的“中間人攻擊”。在身份認證的過程中,出現了一個“中間人”攔截我們的資訊,他有意想要知道你們的訊息。我們將這個中間人稱為M。當伺服器第一次給客戶端傳送公鑰的時候,途徑M。M知道你要進行金鑰交換了,它把公鑰扣了下來,假裝自己是客戶端,偽造了一個偽祕鑰(對稱加密產生的),然後用伺服器發來的公鑰加密了偽祕鑰發還給伺服器,這樣伺服器以為和客戶端完成了金鑰交換,實際上伺服器是和M完成了金鑰交換(獲得了偽祕鑰)。同時M假扮成伺服器自行用非對稱加密產生偽公鑰和偽私鑰,與客戶端進行祕鑰交換,拿到客戶端傳送過來的祕鑰。現在客戶端拿著祕鑰,M拿著祕鑰和為偽祕鑰,伺服器拿著偽祕鑰,整個交流的過程就是:

丁香園iOS電話面試問題總結

還有很多大家直接去大神的blog去看吧,寫的很詳細,我這就點到為止了~~

在不知道二進位制檔案格式的情況下如何區分檔案

當聽到這麼問題的時候還是有點倉促的,隱約記得是通過二進位制的頭部的標識來區分的,當時也不太確定就含糊的回答了一下,說是通過二進位制檔案的頭部標識來區分的,看面試官的樣子不是很滿意,回答百度學習一波,百度結果如下所示,附帶原連結

可以通過二進位制頭識別檔案型別,可以使用UE或者WinHex軟體開啟:

1). JPEG/JPG

  • 檔案頭標識 (2 bytes): $ff, $d8 (SOI) (JPEG檔案標識)
  • 檔案結束標識 (2 bytes): $ff, $d9 (EOI)

2). TGA

  • 未壓縮的前5位元組 00 00 02 0000
  • RLE壓縮的前5位元組 00 00 10 0000

3). PNG

  • 檔案頭標識 (8 bytes) 89 50 4E 470D 0A 1A 0A

4). GIF

  • 檔案頭標識 (6 bytes) 47 49 46 3839(37) 61
    ​ G I F 8 9 (7) a

5). BMP

  • 檔案頭標識 (2 bytes) 424D
    ​ B M

6). PCX

  • 檔案頭標識 (1 bytes) 0A

7). TIFF

  • 檔案頭標識 (2 bytes) 4D 4D 或 4949

8). ICO

  • 檔案頭標識 (8 bytes) 00 00 01 0001 00 20 20

9). CUR

  • 檔案頭標識 (8 bytes) 00 00 02 0001 00 20 20

10). IFF

  • 檔案頭標識 (4 bytes) 46 4F 524D
    ​ F O R M

11). ANI

  • 檔案頭標識 (4 bytes) 52 49 4646
    ​ R I F F

以上是一些檔案的區別方式,回答的總方向還是對的,可能回答的不夠好,下次就知道了。

常見的幾種執行緒鎖

這個問題比較尷尬,因為英文不太好,加上平時用的也不多,回答的比較吞吞吐吐,就說了NSLock@synchronizeddispatch的semaphore,其中有些單詞的讀法還不太準,想想就很尷尬,下面大概總結一下,有一下幾種:

  • NSLock
  • @synchronized
  • dispatch的semaphore
  • 條件鎖NSCondition
  • 條件鎖NSConditionLock
  • NSDistributedLock
  • 互斥鎖POSIX
  • 自旋鎖OSSpinLock

下面總結一下,說實話太多有點記不過來了 - . -,附帶詳細的原文地址

各種執行緒鎖 使用場景和簡單介紹
@synchronized 適用執行緒不多,任務量不大的多執行緒加鎖
NSLock 比較常用的一種鎖,效能一般
dispatch_semaphore_t 使用訊號來做加鎖,效能很好
NSCondition 使用其做多執行緒之間的通訊呼叫不是執行緒安全的
NSConditionLock 單純加鎖效能非常低,比NSLock低很多,但是可以用來做多執行緒處理不同任務的通訊呼叫
NSRecursiveLock 遞迴鎖的效能出奇的高,但是隻能作為遞迴使用,所以限制了使用場景
NSDistributedLock 因為是MAC開發的,就不討論了
POSIX(pthread_mutex) 底層的api,複雜的多執行緒處理建議使用,並且可以封裝自己的多執行緒
OSSpinLock 效能也非常高,可惜出現了執行緒問題

再總結一下,總的意思就是一般用dispatch_semaphore_t就行了,再簡單點用
NSLock,另外帶一個swift出的一個執行緒鎖的方式:

func synchronized(lock: AnyObject, closure: () -> ()) {
    objc_sync_enter(lock)
    closure()
    objc_sync_exit(lock)
}複製程式碼

怎麼保證執行緒安全

這個問題我是接著上一個問題之後回答的,感覺執行緒安全主要是資料競爭帶來的,下面簡單講解一下:

執行緒安全的程式碼可以從多個執行緒或併發任務安全地呼叫,而不會造成任何問題(資料損壞,系統崩潰等)。例如當你多執行緒程式設計時,你用let定義一個陣列,因為它是隻讀的,你能在同一時間不同執行緒去使用它,而不會造成執行緒安全的問題,然而當你用var定義一個陣列時就不一樣了,它不是執行緒安全的,當多個執行緒在同一時間訪問和修改陣列時會產生不可預知的結果。

SDWebImage具體實現和具體類

這個問題回答的一般般吧,說了一下簡單的構造和實現,然後讓我說具體類的時候有點心累了,因為確實記得不是很清楚了,下面簡單總結一下一些主要的類:

  • SDWebImageManager
  • SDWebImageCombinedOperation
  • SDImageCache
  • SDWebImageDownloader
  • 各種類目

這是一些簡單的類,大家想要詳細瞭解可以去這篇文章去看,非常詳細!!!

Alamofire實現原理和主要的類

跟上個問題一樣,簡單的回答了一下,都怪自己沒仔細專研過這些,只是簡單看過,停留在應用層面上,下面同樣簡單介紹一下,附帶大神blog地址吧:

  • Manager
  • SessionDelegate
  • ResponseSerialization
  • URLStringConvertible

這是一些簡單的類,大家想要詳細瞭解可以去這篇文章去看,非常詳細!!!

RxSwift的原理使用和主要類?

這個幸好用過了,不過也沒深入過,就簡單抽象的講了一下響應式程式設計的思想,然後從應用使用方面講解了一下:RxSwift的目的是讓讓資料/事件流和非同步任務能夠更方便的序列化處理,能夠使用swift進行響應式程式設計;讓後讓我說一下RxSwift裡面有哪些Subjects,這個就比較尷尬了,這讓我只用過PublishSubjectDriver的人情何以堪,下面同樣列一下Subjects列表和大神地址

  • PublishSubject

  • ReplaySubject

  • BehaviorSubject

  • Variable

  • Driver

    下面是大神blog,有詳細介紹大家可以去閱讀~~~

realm的簡單介紹和使用時的執行緒問題

這個也簡單用過,也是沒往深入研究,也是大概說了一下使用過程,和多執行緒資料共享的坑,首先realm是一個跨平臺移動資料庫引擎,支援iOSOS XObjective-CSwift)以及Android,核心資料引擎C++打造,並不是建立在SQLite之上的ORM

跨執行緒時的使用

廢話不多說,直接上程式碼:

let person = Person(name: "Jane")
try! realm.write {
  realm.add(person)
}
// 以下是跨執行緒必要的操作,先建一個Reference
let personRef = ThreadSafeReference(to: person)
// 然後在需要返回資料的執行緒裡面去resolve
DispatchQueue(label: "background").async {
    let realm = try! Realm()
    guard let person = realm.resolve(personRef) else {
    // person 已被刪除
      return 
    }
    try! realm.write {
      person.name = "Jane Doe"
    }
}複製程式碼

這裡是官方中文文件,大家可以去看看,非常詳細~~~

簡單講一講RunTime和RunLoop

runtimerrunloop因為看過一篇文章寫的特別好,有一定了解,說了runtime的一些主要功能和應用的地方,下面簡單介紹一下:

RunTime

  • RunTime簡稱執行時;
  • OC就是執行時機制,就是在執行的時候呼叫一些機制;
  • 對於C語言,在編譯的時候會決定呼叫哪個函式;
  • 對於OC,在編譯的時候並不能決定呼叫哪個函式,只有在真正執行的時候才會根據函式的SEL來呼叫對應的函式。

RunTime 應用範圍

  • 傳送訊息
  • 交換方法
  • 動態新增方法
  • 動態新增屬性

其中用的比較多的就是用類目給某個類動態新增屬性。

RunLoop

RunLoop簡單來說就是事件迴圈,保持APP一直處於存活方式的一種機制,讓執行緒能隨時處理事件但並不退出,下面有一篇超級棒的RunLoop文章給大家介紹一下,我這就不展開說了,瞭解RunLoop看那篇文章足夠了。

超級棒的文章地址

簡單說一下iOS的幾種持續化儲存方式

這個回答的就比較輕鬆了,下面隨便列幾個吧,大家有其他的可以補充一下:

  • NSUserDefaults
  • plist
  • NSKeyedArchiver
  • SQL
  • coredata
  • realm

WKWebView快取和清理快取的方法

WKWebView快取的使用

主要通過NSURLCache對請求的資料進行快取,具體實現可以去這個github上去檢視~~~~

清楚快取的方法

詳情看這篇blog,這裡簡單陳述一下,其實在iOS9出了一個方法,呼叫一下就清除了:

NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
//// Date from
NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
//// Execute
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{
// Done
}];複製程式碼

WKWebview的cookie的使用

主要用到的類有NSHTTPCookie,大家同樣也可以去大神的blog去檢視~~~~

autorelease的實現,以及autorelease物件什麼時候會被release

這個問題回答的不太好,說實話當時連續面試了兩家,鬧著已經有點蒙了,這裡借用喵神翻譯的一本書上的原話來描述一下:

autorelease它會將接受該訊息的物件放到一個預先建立的自動釋放池 (auto release pool) 中,並在 自動釋放池收到 drain 訊息時將這些物件的引用計數減一,然後將它們從池子中移除 (這一過程形象地稱為“抽乾池子”)。”

【摘錄來自: 王巍 (onevcat). “Swifter - Swift 必備 Tips (第三版)”。 iBooks. 】

實現原理的話大概就是被autorelease標記的類會被加入一個池子,當這個池子drain時裡面的引用計數會減1。

談談對swift中extension的理解

說實話其實這個問題當時比較吵,聽的不是很清楚,問了好幾遍問題,挺尷尬的,下面簡單介紹一下吧,因為這個在swift中用的比較多:

  • 首先extensionswift中類似oc的類目,可以擴充套件方法,計算屬性,不能新增儲存屬性;
  • 可以通過extension讓類實現某個協議,一般這個用的也比較多,比如實現Comparable這個協議;
  • 還有一個很重要的,就是可以通過extension對協議進行擴充套件,新增預設實現,屬於黑魔法吧,非常好用。

前兩點在面試的時候都又提到,最後一點壓根忘了,自己的面試發揮真不是一般的差- . -

swift寫時複製,自己的結構體怎麼實現寫時複製

幸好之前看了喵神翻譯的【Swift進階】 ,受益頗深,下面同樣借用喵神的話給大家簡單描述一下,大家可以去買這本書,還是挺划算的,下面大量複製喵神書裡面的內容,請見諒:

在 Swift 標準庫中,像是 Array,Dictionary 和 Set 這樣的集合型別是通過一種叫做寫時複製 (copy-on-write) 的技術實現的。我們這裡有一個整數陣列:

var x = [1,2,3]
var y = x複製程式碼

如果我們建立了一個新的變數 y,並且把 x 賦值給它時,會發生複製,現在 x 和 y 含有的是獨立的結構體。在內部,這些 Array 結構體含有指向某個記憶體的引用。這個記憶體就是陣列中元素所儲存的位置,它們位於堆 (heap) 上。在這個時候,兩個陣列的引用指向的是記憶體中同一個位置,這兩個陣列共享了它們的儲存部分。不過,當我們改變 x 的時候,這個共享會被檢測到,記憶體將會被複制。這樣一來,我們得以獨立地改變兩個變數。昂貴的元素複製操作只在必要的時候發生,也就是我們改變這兩個變數的時候發生複製:

x.append(5)
y.removeLast()
x // [1, 2, 3, 5]
y // [1, 2]複製程式碼

如果 Array 結構體中的引用在陣列被改變的一瞬間時是唯一的話 (比如,沒有宣告 y),那麼也不會有複製發生,記憶體的改變將在原地進行。這種行為就是寫時複製,作為一個結構體的作者,你並不能免費獲得這種特性,你需要自己進行實現。當你自己的型別內部含有一個或多個可變引用,同時你想要保持值語義,並且避免不必要的複製時,為你的型別實現寫時複製是有意義的。

下面看看通過簡單的例子看一下:

var array = [COWStruct()]
array[0].change() // No copy

var otherArray = [COWStruct()]
var x = array[0]
x.change() // Copy複製程式碼

然而自己去實現一個寫時複製的話,首先你要判斷引用的唯一性,不是唯一的話進行寫時複製,唯一的話直接改變需要改變的值。

結構體和類以及它們的使用時機

結構體和類主要的區別就是一個是值型別,一個是引用型別;值型別是寫時複製的,引用型別是不會發生寫時複製的;當我們需要一個簡單不需要繼承、不多變的資料時候我們首選結構體,因為在資料結構上來說結構體的存取效率是高於類的,反之當我們需要一個資料結構比較大,需要繼承,變化比較多的時候我們選擇類,因為在變化的過程中結構體可能會發生寫時複製,而類不會;下面舉一個簡單的例子:

ArrayNSMutableArray來說:

當有一個陣列,資料量相對比較小,也不用去經常改變它,只是用來存資料和取資料,我們首先Array

當陣列的資料量很大的時候,並且經常要去對他進行新增,刪除等操作,並且經常賦值給其他變數的話就推薦使用NSMutableArray

讓你實現一個資料儲存的框架的具體思路

這個是一個比較開發性的問題了,我想到的是用策略模式的方式來簡單實現一下,使用策略模式的好處是方便檔案型別的擴充套件,下面我簡單畫個簡單的UML圖大家看一看吧:

丁香園iOS電話面試問題總結

當然這只是一個初步的模型,還有很多細節待考慮,比如檔案快取什麼的,是存本地還是磁碟,這都得去考慮,小弟只是拋磚引玉給個簡單的思路。

寫完已經是深夜了,以上是我丁香園電話面試的一些問題,和之前面試一些回答不好的問題,最後面試完我問了一下丁香園的面試官對我感覺怎樣,他說廣度還行深度不夠,我確實又有這點問題,想學的知識比較多,有時候也沒來得及去看實現原理,只是簡單的過一下,沒深入研究透徹,這是我需要加強的地方,之後如果有二面的話我會在繼續更新的,最後謝謝大家的閱讀~~我是WCL,大家可以去我github關注一波

相關文章