小米iOS開發面試4輪全記錄:Swift與iOS原理深度考察

技術面試作者: 美歷團隊

3年iOS開發小米面試全流程復盤,包含技術一面二面三面、HR面真題,涵蓋Swift高級特性、Runtime、RunLoop、內存管理、UI優化等考點,附面試真題匯總和備考建議

背景介紹

先說下我的情況:3年iOS開發經驗,目前在中型App公司做社交類產品,日活大概200萬左右。日常工作主要用Swift寫業務,也維護一些OC的老模組。說實話,在現在的公司做了兩年多,技術上到了一個瓶頸期——業務迭代很快,但底層的東西接觸得越來越少,感覺自己越來越像「API呼叫工程師」。

今年3月份開始看機會,目標很明確:想去大廠的系統級團隊,能接觸到更底層的iOS開發。小米的MIUI/iOS團隊一直在我的目標列表上,一方面是因為小米生態的廣度,另一方面是聽說他們團隊對iOS原理的考察很深入,正好可以逼自己一把。

投遞過程比較順利,在Boss直聘上和HR聊了之後,很快就安排了面試。整個流程從一面到拿到offer,大概用了兩週半的時間。下面我把4輪面試的完整過程復盤出來,希望對正在準備小米iOS面試或者iOS開發面試的同學有所幫助。

第1輪 技術一面(視訊面,約60分鐘)

一面安排在週一下午2點,面試官是一個看起來很年輕的工程師,自我介紹說是小米iOS基礎架構組的。整體節奏比較緊湊,問題覆蓋面廣,從Swift基礎到iOS原理都有涉及。

1. Swift中struct和class的區別?為什麼Swift推薦優先使用struct?

這個我答得比較流暢:struct是值型別,class是參考型別;struct不支援繼承,class支援;struct有memberwise initializer。推薦優先使用struct的原因主要是值語意更安全、不存在共享狀態導致的副作用、在棧上分配效能更好、天然執行緒安全。面試官追問了copy-on-write機制,我也解釋了Swift標準庫中Array、Dictionary等型別如何透過COW優化效能。

2. Swift泛型中where關鍵字的作用?能舉一個實際使用的例子嗎?

我說where用於對泛型型別新增約束,比如限制泛型參數必須遵循某個協定或繼承某個類別。舉了一個實際例子:寫一個函式要求元素同時遵循Hashable和Comparable協定。面試官點了點頭,沒有深入追問。

3. Swift的protocol和Objective-C的protocol有什麼區別?

我提到了Swift protocol支援預設實作(透過extension)、支援作為泛型約束、支援associatedtype關聯型別。OC的protocol不支援預設實作,也不支援關聯型別。面試官追問了protocol中能不能宣告儲存屬性,我回答不能,只能宣告計算屬性的要求,儲存屬性需要透過關聯物件或者具體型別來實作。

4. Swift的async/await和傳統的GCD/Completion Handler有什麼區別?

我從程式碼可讀性、錯誤處理、任務取消三個維度來對比。async/await讓非同步程式碼看起來像同步程式碼,避免了回呼地獄;搭配try-catch處理錯誤更自然;可以透過Task和TaskGroup實作結構化並發。面試官追問了actor是什麼,我解釋了actor是Swift並發模型中的隔離機制,保證內部狀態的執行緒安全存取,透過actor隔離避免資料競爭。說實話actor這塊我講得不夠深入,只說了基本概念,面試官沒有繼續追問,但我感覺這塊應該再準備得更充分一些。

5. iOS中weak和unowned的區別?分別在什麼場景下使用?

weak是可選型別,物件釋放後自動設為nil;unowned是非可選型別,假設物件始終存在,如果物件釋放後存取會崩潰。使用場景:weak用在閉包中捕獲self時最常見;unowned用在確認物件生命週期比閉包長的場景,比如lazy屬性中。面試官追問了閉包中[weak self]和[unowned self]的選擇,我回答一般優先用weak更安全。

6. SwiftUI和UIKit的區別?你怎麼看SwiftUI的未來?

我說SwiftUI是宣告式UI框架,UIKit是命令式;SwiftUI透過狀態驅動視圖更新,UIKit需要手動管理視圖層級。SwiftUI的優勢是程式碼簡潔、即時預覽、跨平台;劣勢是生態不成熟、複雜自訂困難、效能除錯手段少。關於未來,我認為SwiftUI會逐步替代UIKit,但短期內UIKit仍然是主力,尤其是在複雜業務場景下。面試官似乎對這個回答比較滿意。

7. iOS事件響應鏈(Responder Chain)的工作原理?

我解釋了hit-testing確定第一響應者,然後事件沿響應鏈從子視圖向父視圖傳遞,直到被處理。如果最終沒有被處理,事件會傳遞到UIWindow和UIApplication。面試官追問了如何擴大點擊區域,我說可以重寫point(inside:with:)方法或者使用pointInside的擴展。

8. 你了解iOS中的Method Swizzling嗎?有什麼風險?

我解釋了Method Swizzling是透過Runtime交換兩個方法的實作,常用於AOP程式設計,比如埋點統計。風險包括:交換順序依賴導致的問題、多次交換導致死循環、執行緒安全問題、影響系統行為的不可預測性。建議使用時確保在+load中執行、使用dispatch_once保證只交換一次、做好日誌記錄。面試官對這個回答點了點頭。

9. 簡述iOS的記憶體管理機制ARC的工作原理?

ARC透過編譯器在編譯期自動插入retain/release/autorelease呼叫,基於參考計數管理記憶體。強參考增加參考計數,弱參考不影響。當參考計數降為0時,物件被釋放。我提到了autoreleasepool的作用——在自動釋放池結束時統一發送release訊息,避免記憶體峰值。面試官追問了autoreleasepool在RunLoop中的關係,我簡單說了每次RunLoop迭代會建立一個autoreleasepool,迭代結束時drain。這塊和二面的RunLoop問題有呼應。

第2輪 技術二面(視訊面,約65分鐘)

二面安排在週四上午10點,面試官是團隊的技術負責人,風格明顯比一面更深入,追問很多,會一直挖到你說不出來為止。這輪面試我壓力比較大,有兩個問題答得不太好。

1. 詳細講講Objective-C Runtime的訊息派發機制?

我從objc_msgSend開始講:方法呼叫在編譯期會被轉化為objc_msgSend(receiver, selector, ...),然後依次經歷:在快取中查找方法→在類別的方法列表中查找→沿著繼承鏈向上查找。如果找不到,進入動態方法解析流程:resolveInstanceMethod → forwardingTargetForSelector → forwardInvocation。面試官追問了methodSignatureForSelector和forwardInvocation的關係,我說methodSignatureForSelector回傳方法簽名,forwardInvocation根據簽名執行轉發。然後面試官又問了「如果所有轉發都失敗了會怎樣」,我回答會拋出doesNotRecognizeSelector異常導致崩潰。這塊我整體答得還行,但動態方法解析的細節講得不夠清楚,尤其是resolveInstanceMethod回傳YES後為什麼還要再查一遍快取,我有點含糊。

2. RunLoop的底層機制是什麼?它在iOS中有哪些實際應用?

我說RunLoop本質上是一個事件循環,透過mach_port接收事件,保持執行緒不被銷毀。內部維護了多個Mode,每個Mode包含Source0、Source1、Timer和Observer。實際應用包括:NSTimer的實作、AutoreleasePool的管理、事件響應、GCD回呼的執行。面試官追問了RunLoop和執行緒的關係,我說每個執行緒都有對應的RunLoop,但只有主執行緒的RunLoop預設啟動,子執行緒需要手動獲取並執行。然後面試官問了一個讓我卡住的問題:「RunLoop在休眠時,執行緒處於什麼狀態?是怎麼被喚醒的?」 我知道執行緒會被掛起,但具體說出了「透過mach_msg讓執行緒休眠,當有埠訊息時喚醒」這個細節,但關於核心態和使用者態的切換細節我確實不太清楚,只能坦誠說「這塊我了解得不夠深入」。面試官說沒關係,繼續下一個問題。

3. iOS中如何偵測和解決循環參考(Retain Cycle)?

我列舉了常見場景:閉包捕獲self、delegate宣告為strong、Timer未invalidate。偵測方法:Instruments的Leaks和Allocations工具、Xcode Memory Graph Debugger、FBRetainCycleDetector。解決方法:閉包中使用[weak self]、delegate宣告為weak、Timer在deinit中invalidate。面試官追問了一個我沒想到的問題:「NSTimer的循環參考具體是怎麼形成的?為什麼用weak修飾target不能解決?」 我想了想說,因為NSTimer的target是被RunLoop強持有的,即使target是weak,RunLoop仍然持有timer,timer持有target,形成RunLoop→Timer→Target的循環。weak只能讓參考不增加計數,但RunLoop的持有是strong的。正確的做法是用GCD Timer或者基於Block的Timer API。面試官說「基本正確」,但我感覺解釋得不夠清晰。

4. GCD和OperationQueue的區別和使用場景?

GCD是C語言API,基於佇列和閉包,輕量高效;OperationQueue是物件導向的,支援取消、依賴、優先級、最大並發數控制。使用場景:簡單非同步任務用GCD,複雜任務編排用OperationQueue。面試官追問了GCD的佇列型別和QoS等級,我列舉了main queue、global queue、自訂序列佇列、自訂並發佇列,以及userInteractive、userInitiated、default、utility、background五個QoS等級。

5. 講講iOS中的Category和Extension的區別?Category能新增儲存屬性嗎?

Category在執行時生效,可以為已有類別新增方法,不能直接新增儲存屬性;Extension在編譯時生效,可以新增屬性和方法,但必須在主類別的實作檔案中。Category新增儲存屬性需要透過關聯物件(objc_setAssociatedObject/objc_getAssociatedObject)。面試官追問了關聯物件的底層實作,我說關聯物件儲存在一個全域的AssociationsManager雜湊表中,以物件位址和key為索引。物件釋放時透過objc_destructInstance清理關聯物件。

6. iOS中KVO的底層實作原理?

我說KVO透過Runtime動態建立一個被觀察物件的子類別(NSKVONotifying_XXX),並重寫setter方法,在setter中呼叫willChangeValueForKey和didChangeValueForKey通知觀察者。面試官追問了如何證明這個機制,我說可以透過object_getClass列印物件的實際類別來驗證。還提到了KVO的一個坑:如果在setter外直接修改實例變數,KVO不會觸發,需要手動呼叫willChange/didChange。

第3輪 技術三面(視訊面,約55分鐘)

三面安排在下週二下午3點,面試官是一位資深架構師,問的問題更偏向架構設計和場景分析。這輪面試的節奏比較舒服,面試官更像是在和你討論問題,而不是單純考你。

1. 你在專案中遇到過哪些效能問題?怎麼優化的?

我舉了兩個實際案例。第一個是TableView滑動卡頓:原因是cellForRow中做了圖片解碼和圓角裁剪,最佳化方案是把圖片解碼放到背景執行緒、用預渲染圓角替代即時裁剪、預計算行高。第二個是啟動時間過長:透過新增環境變數DYLD_PRINT_STATISTICS定位到pre-main階段耗時,發現是動態庫太多,合併了幾個內部庫,啟動時間從2.3秒降到1.5秒。面試官追問了還有哪些啟動最佳化手段,我補充了二進位重排、延遲載入非首屏模組、減少+load中的邏輯。

2. 設計一個圖片快取框架,你會怎麼設計?

我從三層快取結構來設計:記憶體快取(NSCache,自動淘汰策略)→磁碟快取(檔案系統,按時間和大小清理)→網路請求(URLSession,支援並發和優先級)。關鍵設計點:記憶體警告時清理記憶體快取、磁碟快取用LRU策略、圖片解碼在背景執行緒、支援漸進式JPEG載入。面試官追問了NSCache和NSDictionary的區別,我說NSCache是執行緒安全的、自動釋放記憶體、不複製key。面試官又問了磁碟快取如何高效查找,我說用檔案路徑的MD5作為檔案名稱,透過檔案系統直接讀取。

3. 如果App出現OOM,你會怎麼排查?

我說首先區分是記憶體洩漏還是記憶體峰值。記憶體洩漏用Instruments Leaks和Memory Graph排查;記憶體峰值需要分析大物件的生命週期。常見原因:大圖片未壓縮直接載入、列表快取未限制大小、循環參考。排查步驟:先用Memory Graph看目前記憶體中的物件分佈,再用Allocations追蹤記憶體增長趨勢,最後用Leaks偵測洩漏。面試官追問了Jetsam機制,我說iOS透過Jetsam優先殺掉記憶體佔用高且優先級低的程序,可以在日誌中查看Jetsam event。

4. 你對組件化有什麼理解?你們專案是怎麼做的?

我說組件化的核心是解耦,讓各模組獨立開發、獨立測試。我們專案用的是基於協定註冊的方案(類似BeeHive),透過Protocol-Class註冊實作服務發現,模組間透過協定通訊而不是直接依賴。面試官問了組件化過程中遇到的困難,我說最大的挑戰是歷史程式碼的解耦——很多跨模組呼叫散落在各處,需要逐步抽離。另一個問題是組件粒度的把握,太細維護成本高,太粗解耦不徹底。

5. 場景題:設計一個可設定的首頁資訊流,支援A/B測試,怎麼設計架構?

我設計了分層架構:設定層(從伺服器端拉取AB實驗設定,決定展示哪些卡片和順序)→資料層(根據設定請求對應的資料來源)→展示層(根據卡片型別動態建立對應的Cell,用工廠模式)。關鍵點:設定下發格式用JSON,客戶端解析後產生卡片模型陣列;Cell註冊用ReuseIdentifier對應;資料來源透過協定抽象,不同卡片實作自己的資料協定。面試官追問了如果新增卡片型別需要發版怎麼辦,我說可以預留通用卡片型別,透過伺服器端設定動態渲染,類似WebView或模板渲染的思路。

第4輪 HR面(約35分鐘)

HR面安排在三面後的第三天,面試官是一位很親切的HR姐姐。整體氛圍比較輕鬆,但有些問題還是需要認真思考後回答。

1. 自我介紹

我簡單介紹了3年iOS開發經驗、目前在做的產品和技術棧、為什麼想加入小米。

2. 為什麼選擇小米?

我說了三點:小米的生態鏈很廣,從手機到IoT到汽車,iOS開發在這裡不只是做App,還有系統級的工作;小米的技術氛圍好,開源文化濃厚;MIUI團隊對技術的追求和我的職業規劃一致。

3. 你在目前公司最大的成就是什麼?

我說了主導App從OC遷移到Swift的專案,歷時半年,遷移了80%的程式碼,同時保證了線上零故障。這個過程中我積累了混合語言專案的經驗,也深入理解了OC和Swift的互操作機制。

4. 你期望的薪資是多少?

我說了目前的薪資和期望漲幅,HR沒有當場回覆,說需要綜合評估後給方案。

5. 你有什麼想問我的?

我問了團隊的技術棧和未來的方向,HR說團隊目前在做跨平台方案的研究,也在參與小米汽車的車機iOS適配。這個回答讓我更加期待了。

面試真題彙總

以下是4輪面試的所有題目,按輪次整理:

技術一面(9題):

1. struct和class的區別,為什麼優先用struct → 考察Swift基礎 → ⭐⭐
2. 泛型where關鍵字的作用和實際例子 → 考察Swift泛型 → ⭐⭐
3. Swift protocol和OC protocol的區別 → 考察語言對比 → ⭐⭐⭐
4. async/await和GCD/Completion Handler的區別 → 考察Swift並發 → ⭐⭐⭐
5. weak和unowned的區別和使用場景 → 考察記憶體管理 → ⭐⭐
6. SwiftUI和UIKit的區別 → 考察技術視野 → ⭐⭐
7. 事件響應鏈的工作原理 → 考察UI原理 → ⭐⭐⭐
8. Method Swizzling的原理和風險 → 考察Runtime → ⭐⭐⭐
9. ARC的工作原理 → 考察記憶體管理 → ⭐⭐

技術二面(6題):

1. Runtime訊息派發機制 → 考察Runtime底層 → ⭐⭐⭐⭐
2. RunLoop的底層機制和實際應用 → 考察RunLoop → ⭐⭐⭐⭐
3. 循環參考的偵測和解決 → 考察記憶體管理 → ⭐⭐⭐
4. GCD和OperationQueue的區別 → 考察多執行緒 → ⭐⭐⭐
5. Category和Extension的區別 → 考察OC特性 → ⭐⭐⭐
6. KVO的底層實作原理 → 考察Runtime → ⭐⭐⭐⭐

技術三面(5題):

1. 專案中的效能最佳化經驗 → 考察實戰能力 → ⭐⭐⭐
2. 設計圖片快取框架 → 考察架構設計 → ⭐⭐⭐⭐
3. OOM排查思路 → 考察效能調優 → ⭐⭐⭐⭐
4. 組件化的理解和實踐 → 考察架構能力 → ⭐⭐⭐
5. 可設定首頁資訊流架構設計 → 考察系統設計 → ⭐⭐⭐⭐⭐

HR面(5題):

1. 自我介紹 → 考察表達能力 → ⭐
2. 為什麼選擇小米 → 考察求職動機 → ⭐⭐
3. 最大成就 → 考察自我認知 → ⭐⭐
4. 薪資期望 → 考察市場認知 → ⭐⭐
5. 反問環節 → 考察主動性 → ⭐

心得體會與建議

最終,我在面試後第5天收到了小米的offer,薪資漲幅約30%,整體比較滿意。回顧整個面試過程,有幾點建議分享給大家:

1. iOS原理一定要深入,不能只停留在表面。小米對Runtime、RunLoop、記憶體管理的考察非常深入,不是背幾個概念就能應付的。比如RunLoop的休眠和喚醒機制、Runtime訊息轉發的完整流程,這些都需要你真正理解底層實作,而不是只記住結論。建議看《Objective-C高級程式設計》和Apple的官方文件,搭配原始碼閱讀。

2. Swift進階特性是加分項,async/await和actor一定要準備。一面中async/await和actor的考察讓我意識到,Swift並發模型已經是面試的高頻考點了。如果你還在只用GCD,建議盡快學習Swift Concurrency,這不僅是面試需要,也是未來iOS開發的方向。

3. 架構設計和場景題需要平時累積,臨時抱佛腳效果有限。三面的圖片快取框架設計和首頁資訊流架構設計,都需要你有實際的專案經驗和設計思維。建議在日常工作中多思考「為什麼這樣設計」,累積設計模式的應用場景,而不是只關注「怎麼實作」。

4. 面試中不會的問題要坦誠,不要硬編。二面中RunLoop的核心態切換我確實不了解,坦誠說了「這塊不夠深入」,面試官並沒有因此扣分。相反,如果你編造答案被追問,反而會嚴重影響評價。不會就說不會,然後表達學習的意願,這是更好的策略。

FAQ

Q1:小米iOS面試對OC基礎要求高嗎?
A:很高。雖然日常開發用Swift,但Runtime、RunLoop、KVO等都是OC層面的底層機制,面試必考。建議即使日常用Swift,也要系統學習OC的Runtime和記憶體管理。

Q2:小米iOS面試會考演算法嗎?
A:我這4輪面試沒有單獨的演算法題,但三面的場景設計題需要你有資料結構和演算法的基礎。建議至少刷完LeetCode Hot 100,重點看鏈結串列、樹、動態規劃。

Q3:SwiftUI在面試中的比重有多大?
A:一面問了一道SwiftUI和UIKit的對比題,不算深。但如果你履歷上寫了SwiftUI專案,面試官肯定會深入問。建議沒有實際專案經驗的話,履歷上不要過度強調SwiftUI。

Q4:小米面試的難度和其他大廠比怎麼樣?
A:個人感覺小米iOS面試的難度中等偏上,比字節跳動的iOS面試略簡單,和百度差不多。特點是原理考察很深,但不會故意刁難,面試官的態度都很好。

Q5:面試前需要準備什麼專案介紹?
A:建議準備1-2個能深入講的專案,重點突出你解決的技術難題和最佳化效果。用STAR法則組織:Situation(背景)→Task(任務)→Action(行動)→Result(結果),尤其是Result要能量化,比如「啟動時間從2.3秒降到1.5秒」比「最佳化了啟動速度」更有說服力。

相關模板

#小米#iOS面試#Swift#Runtime#面試真題