前段時間更新了一篇 給iOS中高級面試官的一份招聘要求 收到不少小夥伴的點贊與關注。可能有不少小夥伴已經帶着我在那篇文章給你們提供的一些面試技巧 & 其中的面試題 已經開始招聘或者應聘了!這裏應你們要求,對裏面的面試題提供相關答案!相信不管是面試官仍是求職者都是有所收穫的~~
PS:篇幅有點長,你們能夠關注或者點贊收藏以備不時之需!!!html
1:講講你對atomic & nonatomic的理解ios
2:被 weak 修飾的對象在被釋放的時候會發生什麼?是如何實現的?知道sideTable 麼?裏面的結構能夠畫出來麼?git
被weak修飾的對象在被釋放時候會置爲nil,不一樣於assign;github
Runtime 維護了一個 weak表,用於存儲指向某個對象的全部 weak指針。weak表 實際上是一個 hash(哈希)表,Key 是所指對象的地址,Value 是 weak指針 的地址(這個地址的值是所指對象指針的地址)數組。web
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 能夠?面試
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 組成的,那麼是按照哪一種規則造成的。
咱們使用一個現實場景來解釋這個問題:當一個用點擊屏幕上的一個按鈕,這個過程具體發生了什麼。
經過兩種方法來作這個事情。
// 先判斷點是否在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
6:談談 KVC 以及 KVO 的理解?
7:RunLoop 的做用是什麼?它的內部工做機制瞭解麼?
字面意思是「消息循環、運行循環」,runloop 內部實際上就是一個 do-while循環 ,它在循環監聽着各類事件源、消息,對他們進行管理並分發給線程來執行。
8:蘋果是如何實現 autoreleasepool 的?
arc下編譯器會優化成
void *context = objc_autoreleasePoolPush();
// {}中的代碼
objc_autoreleasePoolPop(context);
9:談談你對 FRP (函數響應式) 的理解,延伸一下 RxSwift 或者 RAC !
[參考文章:RxSwift(1)— 初探] 看這一篇文章也就夠了!而後結合 RxSwift 映射到 RAC !函數響應式的思想是不變的!至於內部的封裝有所不一樣,可是最終倒是異曲同工!
10:平時開發有沒有玩過 Instrument ?
分析:這裏的內容很是有意思,對於一個iOS高級開發人員,我以爲還有頗有必要掌握的!尤爲開發3-5年,若是沒有掌握這些內容我以爲是不合格的
我我的建議在掌握面試題的同時還須要求職者更多的去分析和拓展!好比你的探索思路,你在這個知識點意外的延伸。還有你再實際開發過程的落地!而這些都是加分項!
1:什麼是 isa,isa 的做用是什麼?
2:一個實例對象的 isa 指向什麼?類對象指向什麼?元類 isa 指向什麼?
類方法:
實例方法:
4: load 和 initialize 的區別?
+load
+initialize
5: _objc_msgForward 函數是作什麼的?直接調用會發生什麼問題?
當對象沒有實現某個方法 ,會調用這個函數進行方法轉發。 (某方法對應的`IMP`沒找到,會返回這個函數的`IMP`去執行)
若是直接調用這個方法,就算實現了想調用的方法,也不會被調用,會直接走消息轉發步驟。
6:簡述下 `Objective-C` 中調用方法的過程
PS: Runtime 鑄就了 Objective-C 是動態語言的特性,使得C語言具有了面向對象的特性,在程序運行期建立,檢查,修改類、對象及其對應的方法,這些操做均可以使用runtime中的對應方法實現。
7:可否想向編譯後獲得的類中增長實例變量?可否向運行時建立的類中添加實例變量?爲何?
解釋:
8:談談你對切面編程的理解
維基百科對於切面編程(AOP)的解釋是這樣的:面向切面的程序設計( aspect-oriented programming,AOP,又譯做面向側面的程序設計、觀點導向編程、剖面導向程序設計)是計算機科學中的一個術語,指一種程序設計範型。該範型以一種稱爲切面的語言構造爲基礎,切面是一種新的模塊化機制,用來描述分散在對象、類、函數)中的橫切關注點。[參考文章]
分析:Runtime 這個模塊iOS面試不管初中高都會面試。我以爲這個模塊不光只是僅僅問問關於知識點內容,我更新想要聽到求職者在這裏面的爬坑探索辛歷路程!Runtime 這個模塊是刷開頁面開發的關鍵點!
1:HTTP的缺陷是什麼?
HTTP 主要有這些不足,例舉以下。
這些問題不只在 HTTP上出現,其餘未加密的協議中也會存在這類問題。
2:談談三次握手,四次揮手!爲何是三次握手,四次揮手?
[參考文章] 我以爲這個地方仍是須要自我理解,用本身的話去表達出來!
3: socket 鏈接和 Http 鏈接的區別
http 是基於 socket 之上的。socket 是一套完整的 tcp,udp 協議的接口。
TCP/IP是傳輸層協議,主要解決數據如何在網絡中傳輸,而HTTP協議是應用層協議,主要解決如何包裝數據。
Socket是對TCP/IP 協議的封裝,它自己不是協議,而是一個調用接口,經過 Socket ,咱們才能使用 TCP/IP協議 。
http 是客戶端用 http 協議進行請求,發送請求時候須要封裝 http 請求頭,並綁定請求的數據,服務器通常有 web 服務器配合。 http 請求方式爲客戶端主動發起請求,服務器才能給響應,一次請求完畢後則斷開鏈接以節省資源。服務器不能主動給客戶端響應。 iPhone 主要使用的類是 NSUrlConnection 。 socket 是客戶端跟服務器直接使用 socket「套接字」 進行拼接,並無規定鏈接後斷開,因此客戶端和服務器能夠保持鏈接,雙方均可以主動發送數據。通常在遊戲開發或者股票開發這種即時性很強的而且保持發送數據量比較大的場合使用。主要類是 CFSocketRef。
4:HTTPS,安全層除了SSL還有,最新的? 參數握手時首先客戶端要發什麼額外參數
5:何時POP網絡,有了 `Alamofire` 封裝網絡 `URLSession`爲何還要用`Moya` ?
POP網絡:面向協議編程的網絡可以大大下降耦合度!網絡層下沉,業務層上浮。中間利用 POP網絡 的 Moya 隔開。若是你的項目是 RxSwift 函數響應式的也沒有關係!由於有 RxMoya
參考文章:
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.數據結構的存儲通常經常使用的有幾種?各有什麼特色?
數據的存儲結構是數據結構的一個重要內容。在計算機中,數據的存儲結構能夠採起以下四中方法來表現。
索引存儲方式
散列存儲方式是根據結點的關鍵字直接計算出該結點的存儲地址的一種存儲的方式。 在實際應用中,每每須要根據具體數據結構來決定採用哪種存儲方式。同一邏輯結構採用不一樣額存儲方法,能夠獲得不一樣的存儲結構。並且這四種節本存儲方法,既能夠單獨使用,也能夠組合起來對數據結構進行存儲描述。
2.集合結構 線性結構 樹形結構 圖形結構 3.單向鏈表 雙向鏈表 循環鏈表 4.數組和鏈表區別 5.堆、棧和隊列
- [iOS經常使用算法和數據結構]
- [數據結構初探]這裏面介紹了不少數據結構,你們還能夠自行查閱更多資料
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.時間複雜度
在[計算機科學]中,時間複雜性,又稱時間複雜度,[算法],它定性描述該算法的運行時間。這是一個表明算法輸入值的[字符串]的長度的函數。時間複雜度經常使用[大O符號]表述,不包括這個函數的低階項和首項係數。使用這種方式時,時間複雜度可被稱爲是[漸近]的,亦即考察輸入值大小趨近無窮時的狀況。 [時間複雜性]
2.空間複雜度
空間複雜度(Space Complexity)是對一個算法在運行過程當中臨時佔用存儲空間大小的量度,記作S(n)=O(f(n))。好比直接[插入排序]的[時間複雜度],空間複雜度是O(1) 。而通常的[遞歸]算法就要有O(n)的空間複雜度了,由於每次遞歸都要存儲返回信息。一個算法的優劣主要從算法的執行時間和所須要佔用的存儲空間兩個方面[衡量]。 [時間複雜度&空間複雜度]
3.經常使用的排序算法
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查找)
兩個思路:
# 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; }
- (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] 思路:
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