給iOS中高級求職者的一份面試題解答

前段時間更新了一篇 給iOS中高級面試官的一份招聘要求 收到不少小夥伴的點贊與關注。可能有不少小夥伴已經帶着我在那篇文章給你們提供的一些面試技巧 & 其中的面試題 已經開始招聘或者應聘了!這裏應你們要求,對裏面的面試題提供相關答案!相信不管是面試官仍是求職者都是有所收穫的~~
PS:篇幅有點長,你們能夠關注或者點贊收藏以備不時之需!!!html

 

iOS基礎


 1:講講你對atomic & nonatomic的理解ios

  •   一、原子操做對線程安全並沒有任何安全保證。被 atomic 修飾的屬性(不重載設置器和訪問器)只保證了對數據讀寫的完整性,也就是原子性,可是與對象的線程安全無關。
  •   二、線程安全有保障、對性能有要求的狀況下可以使用 nonatomic替代atomic,固然也能夠一直使用atomic。
  •   3:[詳細參考]

 2:被 weak 修飾的對象在被釋放的時候會發生什麼?是如何實現的?知道sideTable 麼?裏面的結構能夠畫出來麼?git

被weak修飾的對象在被釋放時候會置爲nil,不一樣於assign;github

Runtime 維護了一個 weak表,用於存儲指向某個對象的全部 weak指針。weak表  實際上是一個 hash(哈希)表,Key 是所指對象的地址,Value 是 weak指針 的地址(這個地址的值是所指對象指針的地址)數組。web

  •  一、初始化時:runtime 會調用  objc_initWeak函數,初始化一個新的 weak指針 指向對象的地址。
  •  二、添加引用時:objc_initWeak函數 會調用 objc_storeWeak() 函數, objc_storeWeak()  的做用是更新指針指向,建立對應的弱引用表。
  •  三、釋放時,調用  clearDeallocating函數 。 clearDeallocating函數 首先根據對象地址獲取全部  weak指針地址 的數組,而後遍歷這個數組把其中的數據設爲  nil,最後把這個  entry 從  weak表 中刪除,最後清理對象的記錄。
  •  4:[詳細參考]
struct SideTable {
// 保證原子操做的自旋鎖
spinlock_t slock; // 引用計數的 hash 表 RefcountMap refcnts; // weak 引用全局 hash 表 weak_table_t weak_table; } struct weak_table_t { // 保存了全部指向指定對象的 weak 指針 weak_entry_t *weak_entries; // 存儲空間 size_t num_entries; // 參與判斷引用計數輔助量 uintptr_t mask; // hash key 最大偏移值 uintptr_t max_hash_displacement; };

3:`block` 用什麼修飾?strong 能夠?面試

  • block 自己是像對象同樣能夠 retain,和 release。可是,block 在建立的時候,它的內存是分配在棧(stack)上,而不是在堆(heap)上。他自己的做於域是屬於建立時候的做用域,一旦在建立時候的做用域外面調用block將致使程序崩潰。
  • 使用 retain 也能夠,可是block的retain行爲默認是用copy的行爲實現的
  • 由於 block 變量默認是聲明爲棧變量的,爲了可以在block的聲明域外使用,因此要把 block 拷貝(copy)到堆,因此說爲了 block 屬性聲明和實際的操做一致,最好聲明爲 copy。
  • [詳細參考]

 4:block 爲何可以捕獲外界變量? __block 作了什麼事?算法

研究Block的捕獲外部變量就要除去函數參數這一項,下面一一根據這4種變量類型的捕獲狀況進行分析。編程

  • 自動變量
  • 靜態變量
  • 靜態全局變量
  • 全局變量

首先 全局變量global_i 和 靜態全局變量static_global_j 的值增長,以及它們被 Block  捕獲進去,這一點很好理解,由於是全局的,做用域很廣,因此 Block 捕獲了它們進去以後,在 Block 裏面進行 ++ 操做, Block 結束以後,它們的值依舊能夠得以保存下來。設計模式

struct __main_block_impl_0 {
struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_a_0 *a; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };

 __main_block_impl_0結構體  就是這樣把自動變量捕獲進來的。也就是說,在執行 Block  語法的時候, Block  語法表達式所使用的自動變量的值是被保存進了 Block 的結構體實例中,也就是  Block 自身中。數組

這裏值得說明的一點是,若是 Block 外面還有不少自動變量,靜態變量,等等,這些變量在 Block 裏面並不會被使用到。那麼這些變量並不會被 Block 捕獲進來,也就是說並不會在構造函數裏面傳入它們的值。

`Block`捕獲外部變量僅僅只捕獲`Block`閉包裏面會用到的值,其餘用不到的值,它並不會去捕獲。

 5:談談你對事件的傳遞鏈和響應鏈的理解

  • 一:響應者鏈  UIResponser 包括了各類 Touch message 的處理,好比開始,移動,中止等等。常見的  UIResponser 有  UIView及子類 ,UIViController , APPDelegate , UIApplication 等等。

回到響應鏈,響應鏈是由 UIResponser 組成的,那麼是按照哪一種規則造成的。

  •  A: 程序啓動  UIApplication 會生成一個單例,並會關聯一個 APPDelegate 。 APPDelegate 做爲整個響應鏈的根創建起來,而 UIApplication 會將本身與這個單例連接,即 UIApplication 的 nextResponser (下一個事件處理者)爲 APPDelegate 。
  •  B:建立UIWindow 程序啓動後,任何的 UIWindow 被建立時, UIWindow 內部都會把 nextResponser 設置爲 UIApplication單例 。 UIWindow 初始化 rootViewController , rootViewController 的 nextResponser 會設置爲 UIWindow 
  •  C:UIViewController初始化  loadView , VC 的 view 的 nextResponser 會被設置爲 VC .
  •  D:addSubView : addSubView 操做過程當中,若是子subView不是VC的View,那麼 subView 的 nextResponser 會被設置爲 superView 。若是是 VC 的 View ,那就是  subView -> subView.VC ->superView 若是在中途, subView.VC 被釋放,就會變成  subView.nextResponser = superView


咱們使用一個現實場景來解釋這個問題:當一個用點擊屏幕上的一個按鈕,這個過程具體發生了什麼。

  • 1. 用戶觸摸屏幕,系統硬件進程會獲取到這個點擊事件,將事件簡單處理封裝後存到系統中,因爲硬件檢測進程和當前App進程是兩個進程,因此進程二者之間傳遞事件用的是端口通訊。硬件檢測進程會將事件放到 APP 檢測的那個端口。
  • 2. APP 啓動主線程 RunLoop 會註冊一個端口事件,來檢測觸摸事件的發生。當事件到達,系統會喚起當前 APP 主線程的 RunLoop 。來源就是 App主線程事件 ,主線程會分析這個事件。
  • 3.最後,系統判斷該次觸摸是否致使了一個新的事件, 也就是說是不是第一個手指開始觸碰,若是是,系統會先從響應網中 尋找響應鏈。若是不是,說明該事件是當前正在進行中的事件產生的一個 Touch message , 也就是說已經有保存好的響應鏈
  • 二:事件傳遞鏈

經過兩種方法來作這個事情。

// 先判斷點是否在View內部,而後遍歷subViews
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; 
//判斷點是否在這個View內部
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event; // default returns YES if point is in bounds

 

  • A: 流程
  • 1:先判斷該層級是否可以響應`(1.alpha>0.01 2.userInteractionEnabled == YES 3.hidden = NO)`
  • 2:判斷改點是否在`view`內部,
  • 3:若是在那麼遍歷子`view`繼續返回可響應的view,直到沒有。
  • B:常見問題
  • 父view設置爲不可點擊,子view能夠點擊嗎
  • 不能夠,hit test 到父view就截止了
  • 子view設置view不可點擊不影響父類點擊
  • 同父view覆蓋不影響點擊
  • 手勢對responder方法的影響
  • C:實際用法
  • 點一一個圓形控件,如何實現只點擊圓形區域有效,重載`pointInside`。此時可將外部的點也判斷爲內部的點,反之也能夠。
  • 事件響應鏈在複雜功能界面進行不一樣控件間的通訊,簡便某些場景下優於代理和`block`

 6:談談 KVC 以及 KVO 的理解?

7:RunLoop 的做用是什麼?它的內部工做機制瞭解麼?

字面意思是「消息循環、運行循環」,runloop 內部實際上就是一個 do-while循環 ,它在循環監聽着各類事件源、消息,對他們進行管理並分發給線程來執行。

  •  1.通知觀察者將要進入運行循環。 線程和 RunLoop 之間是一一對應的
  •  2.通知觀察者將要處理計時器。
  •  3.通知觀察者任何非基於端口的輸入源即將觸發。
  •  4.觸發任何準備觸發的基於非端口的輸入源。
  •  5.若是基於端口的輸入源準備就緒並等待觸發,請當即處理該事件。轉到第9步。
  •  6.通知觀察者線程即將睡眠。
  •  7.將線程置於睡眠狀態,直到發生如下事件之一:
  1.  事件到達基於端口的輸入源。
  2.  計時器運行。
  3.  爲運行循環設置的超時值到期。
  4.  運行循環被明確喚醒。
  •  8.通知觀察者線程被喚醒。
  •  9.處理待處理事件。
  1.  若是觸發了用戶定義的計時器,則處理計時器事件並從新啓動循環。轉到第2步。
  2.  若是輸入源被觸發,則傳遞事件。
  3.  若是運行循環被明確喚醒但還沒有超時,請從新啓動循環。轉到第2步。
  •  10.通知觀察者運行循環已退出。

8:蘋果是如何實現 autoreleasepool 的?

arc下編譯器會優化成

void *context = objc_autoreleasePoolPush();
// {}中的代碼
objc_autoreleasePoolPop(context);
  • 向一個結構 AutoreleasePoolPage,中寫入須要自動釋放的對象,相似一種標記,調用 objc_autoreleasePoolPop(context) 後,就會把這中間的對象 release 一下。
  • 這裏要注意的是,方法返回值是怎麼作到自動釋放的?
  • 其使用 Thread Local Storage(TLS) 線程局部存儲,每次存入線程或者從線程取出來。
  • 咱們沒有卸載`{}`中的自動釋放對象,會在每一個 runloop 結束時候去釋放,至關於一個大的 autoreleasepool 中。
  • [參考文章]
  • [蘋果是如何實現autoreleasepool的]

9:談談你對  FRP (函數響應式)  的理解,延伸一下  RxSwift  或者  RAC !

[參考文章:RxSwift(1)— 初探] 看這一篇文章也就夠了!而後結合  RxSwift  映射到  RAC !函數響應式的思想是不變的!至於內部的封裝有所不一樣,可是最終倒是異曲同工!

10:平時開發有沒有玩過 Instrument ?

分析:這裏的內容很是有意思,對於一個iOS高級開發人員,我以爲還有頗有必要掌握的!尤爲開發3-5年,若是沒有掌握這些內容我以爲是不合格的

 我我的建議在掌握面試題的同時還須要求職者更多的去分析和拓展!好比你的探索思路,你在這個知識點意外的延伸。還有你再實際開發過程的落地!而這些都是加分項!

Runtime


 1:什麼是 isa,isa 的做用是什麼?

2:一個實例對象的 isa 指向什麼?類對象指向什麼?元類 isa 指向什麼?

  • [參考文章]
  • 實例對象的 isa 指向類
  • 類對象的 isa 指向元類
  • 元類isa 指向根元類

  • 3:objc  中類方法和實例方法有什麼本質區別和聯繫?
  •  [參考文章]

類方法:

  •  1.類方法是屬於類對象的
  •  2.類方法只能經過類對象調用
  •  3.類方法中的self是類對象
  •  4.類方法能夠調用其餘的類方法
  •  5.類方法中不能訪問成員變量
  •  6.類方法中不能直接調用對象方法

實例方法:

  •  1.實例方法是屬於實例對象的
  •  2.實例方法只能經過實例對象調用
  •  3.實例方法中的self是實例對象
  •  4.實例方法中能夠訪問成員變量
  •  5.實例方法中直接調用實例方法
  •  6.實例方法中也能夠調用類方法(經過類名)

 4: load  和  initialize 的區別?

+load

  •  一、只要程序啓動就會將全部類的代碼加載到內存中(在main函數執行以前), 放到代碼區(不管該類有沒有被使用到都會被調用)
  •  二、 +load 方法會在當前類被加載到內存的時候調用, 有且僅會調用一次
  •  三、當父類和子類都實現`+load`方法時, 會先調用父類的 +load 方法, 再調用子類的 +load 方法
  •  四、先加載原始類,再加載分類的 +load 方法
  •  五、當子類未實現 +load 方法時,不會調用父類的 +load 方法
  •  六、多個類都實現 +load 方法, +load 方法的調用順序,與 Compile Sources 中出現的順序一致

[load方法在Apple官方文檔中的描述]

+initialize

  •  一、當類第一次被使用的時候就會調用(建立類對象的時候)
  •  二、`initialize`方法在整個程序的運行過程當中只會被調用一次, 不管你使用多少次這個類都只會調用一次
  •  三、`initialize`用於對某一個類進行一次性的初始化
  •  四、先調用父類的 initialize 再調用子類的 initialize
  •  五、當子類未實現 initialize 方法時,會把父類的實現繼承過來調用一遍,再次以前父類的 initialize 方法會被優先調用一次
  •  六、當有多個 Category 都實現了 initialize 方法,會覆蓋類中的方法,只執行一個(會執行 Compile Sources  列表中最後一個 Category  的 initialize 方法)

[initialize方法在Apple官方文檔中的描述]

 5: _objc_msgForward  函數是作什麼的?直接調用會發生什麼問題?

當對象沒有實現某個方法 ,會調用這個函數進行方法轉發。 (某方法對應的`IMP`沒找到,會返回這個函數的`IMP`去執行)

  •  1.調用resolveInstanceMethod:方法,容許用戶在此時爲該Class動態添加實現。若是有實現了,則調用並返回。若是仍沒實現,繼續下面的動做。
  •  2.調用forwardingTargetForSelector:方法,嘗試找到一個能響應該消息的對象。若是獲取到,則直接轉發給它。若是返回了nil,繼續下面的動做。
  •  3.調用methodSignatureForSelector:方法,嘗試得到一個方法簽名。若是獲取不到,則直接調用doesNotRecognizeSelector拋出異常。
  •  4.調用forwardInvocation:方法,將地3步獲取到的方法簽名包裝成Invocation傳入,如何處理就在這裏面了。

若是直接調用這個方法,就算實現了想調用的方法,也不會被調用,會直接走消息轉發步驟。

 6:簡述下 `Objective-C` 中調用方法的過程

  •  Objective-C 是動態語言,每一個方法在運行時會被動態轉爲消息發送,即: objc_msgSend(receiver, selector) ,整個過程介紹以下:
  •  objc 在向一個對象發送消息時,`runtime`庫會根據對象的isa指針找到該對象實際所屬的類
  •  而後在該類中的方法列表以及其父類方法列表中尋找方法運行
  •  若是,在最頂層的父類(通常也就NSObject)中依然找不到相應的方法時,程序在運行時會掛掉並拋出異常  unrecognized selector sent to XXX
  • 可是在這以前,objc的運行時會給出三次拯救程序崩潰的機會,這三次拯救程序奔潰的說明見問題《何時會報unrecognized selector的異常》中的說明

PS: Runtime  鑄就了 Objective-C 是動態語言的特性,使得C語言具有了面向對象的特性,在程序運行期建立,檢查,修改類、對象及其對應的方法,這些操做均可以使用runtime中的對應方法實現。

7:可否想向編譯後獲得的類中增長實例變量?可否向運行時建立的類中添加實例變量?爲何?

  • 1.不能向編譯後獲得的類增長實例變量
  • 2.能向運行時建立的類中添加實例變量

解釋:

  • 1.編譯後的類已經註冊在 runtime 中,類結構體中的 objc_ivar_list 實例變量的鏈表和 instance_size 實例變量的內存大小已經肯定,  runtime 會調用  class_setvarlayout  或  class_setWeaklvarLayout 來處理 strong weak 引用.因此不能向存在的類中添加實例變量
  • 2.運行時建立的類是能夠添加實例變量,調用 class_addIvar函數 .可是的在調用 objc_allocateClassPair 以後, objc_registerClassPair 以前,緣由同上.

8:談談你對切面編程的理解

維基百科對於切面編程(AOP)的解釋是這樣的:面向切面的程序設計( aspect-oriented programming,AOP,又譯做面向側面的程序設計、觀點導向編程、剖面導向程序設計)是計算機科學中的一個術語,指一種程序設計範型。該範型以一種稱爲切面的語言構造爲基礎,切面是一種新的模塊化機制,用來描述分散在對象、類、函數)中的橫切關注點。[參考文章]


 分析:Runtime 這個模塊iOS面試不管初中高都會面試。我以爲這個模塊不光只是僅僅問問關於知識點內容,我更新想要聽到求職者在這裏面的爬坑探索辛歷路程!Runtime 這個模塊是刷開頁面開發的關鍵點!

 網絡&多線程


1:HTTP的缺陷是什麼?

HTTP 主要有這些不足,例舉以下。

  •  通訊使用明文(不加密),內容可能會被竊聽
  •  不驗證通訊方的身份,所以有可能遭遇假裝
  •  沒法證實報文的完整性,因此有可能已遭篡改

這些問題不只在 HTTP上出現,其餘未加密的協議中也會存在這類問題。

 2:談談三次握手,四次揮手!爲何是三次握手,四次揮手?

[參考文章] 我以爲這個地方仍是須要自我理解,用本身的話去表達出來!

 3: socket  鏈接和 Http 鏈接的區別

http 是基於 socket 之上的。socket 是一套完整的 tcp,udp 協議的接口。

  • HTTP協議:簡單對象訪問協議,對應於應用層,HTTP 協議是基於TCP鏈接的。
  • tcp協議:對應於傳輸層。
  • ip協議:對應於網絡層。

TCP/IP是傳輸層協議,主要解決數據如何在網絡中傳輸,而HTTP協議是應用層協議,主要解決如何包裝數據。

Socket是對TCP/IP 協議的封裝,它自己不是協議,而是一個調用接口,經過 Socket ,咱們才能使用 TCP/IP協議 。

  • http鏈接:就是所謂的短鏈接,即客戶端向服務器端發送一次請求,服務器端響應後鏈接即會斷掉。
  • socket鏈接:就是所謂的長鏈接,理論上客戶端和服務器端一旦創建起鏈接將不會主動斷掉,可是因爲各類環境因素可能會使鏈接斷開。

 http 是客戶端用 http 協議進行請求,發送請求時候須要封裝 http 請求頭,並綁定請求的數據,服務器通常有 web 服務器配合。 http 請求方式爲客戶端主動發起請求,服務器才能給響應,一次請求完畢後則斷開鏈接以節省資源。服務器不能主動給客戶端響應。 iPhone 主要使用的類是 NSUrlConnection 。 socket 是客戶端跟服務器直接使用 socket「套接字」 進行拼接,並無規定鏈接後斷開,因此客戶端和服務器能夠保持鏈接,雙方均可以主動發送數據。通常在遊戲開發或者股票開發這種即時性很強的而且保持發送數據量比較大的場合使用。主要類是 CFSocketRef。

  • UDP:是用戶數據報協議:主要用在實時性要求高以及對質量相對較弱的地方,但面對如今高質量的線路容易丟包。
  • TCP:是傳輸控制協議,是面向鏈接的,,運行環境必然要求其可靠性不可丟失包有良好的擁塞控制機制。

4:HTTPS,安全層除了SSL還有,最新的? 參數握手時首先客戶端要發什麼額外參數

 

 5:何時POP網絡,有了 `Alamofire` 封裝網絡 `URLSession`爲何還要用`Moya` ?

 POP網絡:面向協議編程的網絡可以大大下降耦合度!網絡層下沉,業務層上浮。中間利用  POP網絡 的 Moya 隔開。若是你的項目是  RxSwift  函數響應式的也沒有關係!由於有  RxMoya 

參考文章:

  • * [Moya]()
  • * [Swift - 面向協議編程(POP)](https://juejin.im/post/5d6e6b10f265da038f482a91)
  • * [Swift - 當Moya趕上RxSwift(網絡架構優化)](https://juejin.im/post/5d6fb6ef51882520d46ab27e)


 6:如何實現  dispatch_once 

 

+ (instancetype)sharedInstance
{
/*定義相應類實例的靜態變量;
意義:函數內定義靜態變量,不管該函數被調用多少次,
在內存中只初始化一次,而且能保存最後一次賦的值
*/
static ClassName *instance = nil; /*定義一個dispatch_once_t(其實也就是整型)靜態變量, 意義:做爲標識下面dispatch_once的block是否已執行過。 static修飾會默認將其初始化爲0,當值爲0時纔會執行block。 當block執行完成,底層會將onceToken設置爲1,這也就是爲什 麼要傳onceToken的地址(static修飾的變量能夠經過地址修改 onceToken的值),同時底層會加鎖來保證這個方法是線程安全的 */ static dispatch_once_t onceToken; /*只要當onceToken == 0時纔會執行block,不然直接返回靜態變量instance*/ dispatch_once(&onceToken, ^{ instance = [[ClassName alloc] init]; //... }); return instance; }

[iOS原理之CGD-dispatch_once的底層實現]

7:可否寫一個讀寫鎖?談談具體的分析

8:何時會出現死鎖?如何避免?

9:有哪幾種鎖?各自的原理?它們之間的區別是什麼?最好能夠結合使用場景來講

分析:這個模塊多是通常開發人員的盲區。對於這一塊必定要有本身的理解!學習的方向就是查漏補缺,一步一個吃掉!若是你一整塊去啃,你會發現很枯燥!雖然開發過程當中你可能用不到,可是面試這一塊是你必需要掌握的!

 數據結構


 1.數據結構的存儲通常經常使用的有幾種?各有什麼特色?

數據的存儲結構是數據結構的一個重要內容。在計算機中,數據的存儲結構能夠採起以下四中方法來表現。

  •  順序存儲方式:簡單的說,順序存儲方式就是在一塊連續的存儲區域 一個接着一個的存放數據。順序存儲方式把邏輯上相連的結點存儲在物理位置上相鄰的存儲單元裏,結點間的邏輯關係由存儲單元的鄰接掛安息來體現。順序存儲方式也稱爲順序存儲結構(sequentialstorage structure),通常採用數組或者結構數組來描述。 線性存儲方式主要用於線性邏輯結構的數據存放,而對於圖和樹等非線性邏輯結構則不適用。
  •  連接存儲方式:連接存儲方式比較靈活,其不要求邏輯上相鄰的結點在物理位置上相鄰,結點間的邏輯關係由附加的引用字段表示。一個結點的引用字段每每指導下一個結點的存放位置。 連接存儲方式也稱爲連接式存儲結構(LinkedStorage Structure),通常在原數據項中增長應用類型來表示結點之間的位置關係。

 索引存儲方式

  • 索引存儲方式是採用附加索引表的方式來存儲結點信息的一種存儲方式。索引表由若干個索引項組成。索引存儲方式中索引項的通常形式爲:(關鍵字、地址)。其中,關鍵字是可以惟一標識一個結點的數據項。
  •  索引存儲方式還能夠細分爲以下兩類:
  •  稠密索引(Dense Index):這種方式中每一個結點在索引表中都有一個索引項。其中,索引項的地址指示結點所在的的存儲位置;
  •  稀疏索引(Spare Index):這種方式中一組結點在索引表中只對應一個索引項。其中,索引項的地址指示一組結點的起始存儲位置。
  •  散列存儲方式

散列存儲方式是根據結點的關鍵字直接計算出該結點的存儲地址的一種存儲的方式。 在實際應用中,每每須要根據具體數據結構來決定採用哪種存儲方式。同一邏輯結構採用不一樣額存儲方法,能夠獲得不一樣的存儲結構。並且這四種節本存儲方法,既能夠單獨使用,也能夠組合起來對數據結構進行存儲描述。

 2.集合結構 線性結構 樹形結構 圖形結構 3.單向鏈表 雙向鏈表 循環鏈表 4.數組和鏈表區別 5.堆、棧和隊列

 

6.輸入一棵二叉樹的根結點,求該樹的深度?

若是一棵樹只有一個結點,它的深度爲1。 若是根結點只有左子樹而沒有右子樹, 那麼樹的深度應該是其左子樹的深度加1,一樣若是根結點只有右子樹而沒有左子樹,那麼樹的深度應該是其右子樹的深度加1\. 若是既有右子樹又有左子樹, 那該樹的深度就是其左、右子樹深度的較大值再加1。

public static int treeDepth(BinaryTreeNode root) {
if (root == null) { return 0; } int left = treeDepth(root.left); int right = treeDepth(root.right); return left > right ? (left + 1) : (right + 1); }

 7.輸入一課二叉樹的根結點,判斷該樹是否是平衡二叉樹?

  • (1)須要重複遍歷節點屢次的解法
  • (2)每一個節點只需遍歷一次的解法
  •  [參考文章]

算法


1.時間複雜度

在[計算機科學]中,時間複雜性,又稱時間複雜度,[算法],它定性描述該算法的運行時間。這是一個表明算法輸入值的[字符串]的長度的函數。時間複雜度經常使用[大O符號]表述,不包括這個函數的低階項和首項係數。使用這種方式時,時間複雜度可被稱爲是[漸近]的,亦即考察輸入值大小趨近無窮時的狀況。 [時間複雜性]

2.空間複雜度

空間複雜度(Space Complexity)是對一個算法在運行過程當中臨時佔用存儲空間大小的量度,記作S(n)=O(f(n))。好比直接[插入排序]的[時間複雜度],空間複雜度是O(1) 。而通常的[遞歸]算法就要有O(n)的空間複雜度了,由於每次遞歸都要存儲返回信息。一個算法的優劣主要從算法的執行時間和所須要佔用的存儲空間兩個方面[衡量]。 [時間複雜度&空間複雜度]

3.經常使用的排序算法

  • 一、冒泡排序
  • 二、選擇排序
  • 三、插入排序
  • 四、希爾排序
  • 五、快速排序
  • 六、歸併排序
  • 七、堆排序
  • [常見的7種排序算法]

 4.字符串反轉

- (NSString *)reversalString:(NSString *)originString{
NSString *resultStr = @""; for (NSInteger i = originString.length -1; i >= 0; i--) { NSString *indexStr = [originString substringWithRange:NSMakeRange(i, 1)]; resultStr = [resultStr stringByAppendingString:indexStr]; } return resultStr; }

5.鏈表反轉(頭差法)

public Node reverseList(){
Node cur = head; Node prev = null; Node curNext = head.next; Node reverHead = null; while(cur!=null){ cur.next = prev; cur = curNext; prev = cur; curNext = curNext.next; } reverHead = cur; return reverHead; }

 6.有序數組合並

objc
- (void)merge { /* 有序數組A:一、四、五、八、10...1000000,有序數組B:二、三、六、七、9...999998,A、B兩個數組不相互重複,請合併成一個有序數組C,寫出代碼和時間複雜度。 */ //(1). NSMutableArray *A = [NSMutableArray arrayWithObjects:@4,@5,@8,@10,@15, nil]; // NSMutableArray *B = [NSMutableArray arrayWithObjects:@2,@6,@7,@9,@11,@17,@18, nil]; NSMutableArray *B = [NSMutableArray arrayWithObjects:@2,@6,@7,@9,@11,@12,@13, nil]; NSMutableArray *C = [NSMutableArray array]; int count = (int)A.count+(int)B.count; int index = 0; for (int i = 0; i < count; i++) { if (A[0]<B[0]) { [C addObject:A[0]]; [A removeObject:A[0]]; } else if (B[0]<A[0]) { [C addObject:B[0]]; [B removeObject:B[0]]; } if (A.count==0) { [C addObjectsFromArray:B]; NSLog(@"C = %@",C); index = i+1; NSLog(@"index = %d",index); return; } else if (B.count==0) { [C addObjectsFromArray:A]; NSLog(@"C = %@",C); index = i+1; NSLog(@"index = %d",index); return; } } //(2). //時間複雜度 //T(n) = O(f(n)):用"T(n)"表示,"O"爲數學符號,f(n)爲同數量級,通常是算法中頻度最大的語句頻度。 //時間複雜度:T(n) = O(index); }

7.查找第一個只出現一次的字符(Hash查找)

兩個思路:

  • 1: hash �不一樣編譯器對字符數據的處理不同,因此hash以前先把字符類型轉成無符號類型;
  • 2,空間換時間,用`buffer數組`記錄當前只找到一次的字符,避免二次遍歷。
# define SIZE 256
char GetChar(char str[])
{
if(!str) return 0; char* p = NULL; unsigned count[SIZE] = {0}; char buffer[SIZE]; char* q = buffer; for(p=str; *p!=0; p++) { if(++count[(unsigned char)*p] == 1) *q++ = *p; } for (p=buffer; p<q; p++) { if(count[(unsigned char)*p] == 1) return *p; } return 0; }

8.查找兩個子視圖的共同父視圖

這個問的實際上是數據結構中的二叉樹,查找一個普通二叉樹中兩個節點最近的公共祖先問題 假設兩個視圖爲 UIViewA 、 UIViewC ,其中  UIViewA 繼承於 UIViewB , UIViewB 繼承於 UIViewD , UIViewC 也繼承於 UIViewD ;即  A->B->D,C->D 

- (void)viewDidLoad {
[super viewDidLoad];
Class commonClass1 = [self commonClass1:[ViewA class] andClass:[ViewC class]]; NSLog(@"%@",commonClass1); // 輸出:2018-03-22 17:36:01.868966+0800 兩個UIView的最近公共父類[84288:2458900] ViewD } // 獲取全部父類 - (NSArray *)superClasses:(Class)class { if (class == nil) { return @[]; } NSMutableArray *result = [NSMutableArray array]; while (class != nil) { [result addObject:class]; class = [class superclass]; } return [result copy]; } - (Class)commonClass1:(Class)classA andClass:(Class)classB { NSArray *arr1 = [self superClasses:classA]; NSArray *arr2 = [self superClasses:classB]; for (NSUInteger i = 0; i < arr1.count; ++i) { Class targetClass = arr1[i]; for (NSUInteger j = 0; j < arr2.count; ++j) { if (targetClass == arr2[j]) { return targetClass; } } } return nil; }
  • 方法一明顯的是兩層for循環,時間複雜度爲  O(N^2)  一個改進的辦法:咱們將一個路徑中的全部點先放進NSSet中.由於NSSet的內部實現是一個hash表,因此查詢元素的時間的複雜度變成  O(1) ,咱們一共有N個節點,因此總時間複雜度優化到了 O(N) 
- (Class)commonClass2:(Class)classA andClass:(Class)classB{
NSArray *arr1 = [self superClasses:classA]; NSArray *arr2 = [self superClasses:classB]; NSSet *set = [NSSet setWithArray:arr2]; for (NSUInteger i =0; i<arr1.count; ++i) { Class targetClass = arr1[i]; if ([set containsObject:targetClass]) { return targetClass; } } return nil; }

 9.無序數組中的中位數(快排思想)

[參考:求無序數組中的中位數]

 10.給定一個整數數組和一個目標值,找出數組中和爲目標值的兩個數。

你能夠假設每一個輸入只對應一種答案,且一樣的元素不能被重複利用。 示例:給定 nums = [2, 7, 11, 15], target = 9  ---  返回 [0, 1]  思路:

  •  第一層for循環從索引0到倒數第二個索引拿到每一個數組元素,
  •  第二個for循環遍歷上一層for循環拿到的元素的後面的全部元素。
  •  [參考文章]
class Solution {
public int[] twoSum(int[] nums, int target) { int len = nums.length; int[] result = new int[2]; for(int i = 0; i < len; i++){ for(int j = i+1; j < len; j++){ if(nums[i] + nums[j] == target){ result[0] = i; result[1] = j; return result; } } } return result; } }

分析:這個模塊是絕大部分開發人員的軟肋!這個模塊是最能測試求職者思惟能力的!可是我不建議面試官直接讓求職者手寫 在那樣的面試緊張環境,手寫數據結構或者一些算法代碼,是很是有挑戰的!思惟到我以爲差很少!

架構設計


1:設計模式是爲了解決什麼問題的?

設計模式(Design pattern)是一套被反覆使用、多數人知曉的、通過分類編目的、代碼設計經驗的總結。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。

設計模式最主要解決的問題是經過封裝和隔離變化點來處理軟件的各類變化問題。 隔離變化的好處在於,將系統中常常變化的部分和穩定的部分隔離,有助於增長複用性,並下降系統耦合度。不少設計模式的意圖中都明顯地指出了其對問題的解決方案,學習設計模式的要點是發現其解決方案中封裝的變化點。

三本經典書籍:[《GOF設計模式》],[《設計模式解析》],《Head First Design Pattern》

設計模式是軟件開發領域的精髓之一。學好設計模式是目前每個開發人員的必修課,

 2:看過哪些第三方框架的源碼,它們是怎麼設計的?

這個題目就看你我的的感觸,考量你平時的功底! 你們能夠針對性一些常見的框架: RxSwift 、 Alamofire 、 Moya 、 AFNetworing 、 YYKit .... 掌握會用的同時,必需要掌握底層的核心思想!

3:能夠說幾個重構的技巧麼?你以爲重構適合何時來作?

  •  重複代碼的提煉
  •  冗長方法的分割
  •  嵌套條件分支的優化
  •  去掉一次性的臨時變量
  •  消除過長參數列表
  •  提取類或繼承體系中的常量
  •  讓類提供應該提供的方法
  •  拆分冗長的類
  •  提取繼承體系中重複的屬性與方法到父類

在新功能增長時候,在擴展再也不簡單的時候。重構是一個不斷的過程。

4:開發中經常使用架構設計模式你怎麼選型?

這裏也是一道開放性題目!並非說某一種架構就是最優秀的~只有最合適的!根據公司狀況,項目現狀,以及開發者水平及時調整,設計!

 5:你是如何組件化解耦的?

 iOS 解藕 、組件化最經常使用的是使用統跳路由的方式,目前比較經常使用的 iOS 開源路由框架主要是 JLRoutes 、 MGJRouter 、 HHRouter 等,這些路由框架各有優勢和缺點,基本能夠知足大部分需求。目前最經常使用來做路由跳轉,以實現基本的組件化開發,實現各模塊之間的解藕。可是,在實際中開發中會發現,沒法完全使用它們完成全部模塊間通訊,好比模塊間的同步、異步通訊等。再好比,咱們在配置了相關路由跳轉的 URL 後,如何在上線以後動態修改相關跳轉邏輯?在模塊間通訊時,如何在上線後動態修改相關參數?APP 可否實現相似  Web 的302跳轉 ?[學習參考]

分析:架構設計這一層對於一個iOS中高級開發人員來講。這一塊那是他必需要去思考和感覺總結的!若是這位求職者開發4-5年了,一直都在作應用層界面開發,那麼想必他將來的職業晉升是已經落後了的!面試官不妨在這一個模塊單獨設計成一面,就和求職者一塊兒交流討論。畢竟這些思惟的設計,也許可以給面試官帶來一些不同的東西!😊

 性能優化

1:`tableView` 有什麼好的性能優化方案?

2: 界面卡頓和檢測你都是怎麼處理?

3:談談你對離屏渲染的理解?

4:如何下降APP包的大小

5:平常如何檢查內存泄露?

6:APP啓動時間應從哪些方面優化?

分析:如今APP性能優化以及成爲iOS中高級開發人員必需要去關係的東西!這一塊我我的建議結合實際開發去和求職者交流。而不是僅僅停留在知識點問答,由於沒有實際開發能力的性能優化都只是紙上談兵!

總結

這一套面試題仍是有必定的水平和難度的!可是對於要應聘一份iOS中高級開發崗位,仍是比較中肯的!但願你們可以在接下來的跳槽漲薪有本身的思想。

文章有長,建議關注備份,無論是正在面試仍是即將面試,應該對你有幫助,既然看到這裏:麻煩點個贊吧!👍

 PS:對本文內容存在疑問還望指出,謝謝!加油,靜候你的佳音

 多關注我,後續會慢慢分享獨立開發者心得和乾貨(掃碼加我,拉你進微信羣)

做者:Cooci連接:https://juejin.im/post/5d8e150d518825097013297d

相關文章
相關標籤/搜索