最近在技術群看到的4道面試題。感覺挺難的,就google+自己測+交流然後得出的自己的見解,有什麼錯誤,歡迎指出。
1.NSDictionary的資料結構與演算法:
Internally, a dictionary uses a hash table to organize its storage and to provide rapid access to a value given the corresponding key
複製程式碼
蘋果官方給出的資料表明是使用hash table進行資料儲存和訪問的。
2.類方法,例項方法與runtime的聯絡: 這題在我看了應該是最簡單的,因為平時接觸runtime比較多,所以大致瞭解一點。這道題應該這麼問:runtime是怎樣呼叫類方法和例項方法的。 這個答案一搜一大堆,但是我還是要說一下我的見解。
1.當呼叫了一個方法時,objc_msgSend通過物件的isa指標獲取到類的結構體,在結構體的struct objc_cache *cache
中尋找是否有該方法的快取,如果cache沒有,才去struct objc_method_list **methodLists
中查詢方法。
2.假如沒找到,就會去這個類的父類的isa指標找到其父類的結構體,並在父類的分發表裡面查詢方法的selector。
3.一旦遍歷搜尋到與SEL相符的selector,就會根據這個selector獲取到對應的imp指標,從而找到函式實現的起始位置。
4.假如沒有找到就會進入訊息轉發流程。呼叫_objc_msgForward
函式,這個函式大致會呼叫resolveInstanceMethod,forwardingTargetForSelector,methodSignatureForSelector,forwardInvocation,這些方法都可以在子類中過載。
注意:假如使用Category過載了類的對應方法,runtime只是把這個Category的方法加到原方法的前面。因此搜尋SEL的時候會先匹配到Category的方法,但是原方法還在,依舊可以實現。
類方法則存放在class的metaClass的方法列表中。與例項方法的搜尋方式一致。
3.block呼叫的變數的生命週期: Block本質是一個指向結構體的指標,在初始化block的時候,當block引用了外部變數時,會根據是否加了__block欄位來決定是否是值傳入,還是地址傳入。 在 ARC 中,捕獲外部變數的 block 的類會是 NSMallocBlock 或者 NSStackBlock,如果 block 被賦值給了某個變數在這個過程中會執行 _Block_copy 將原有的 NSStackBlock 變成 NSMallocBlock;但是如果 block 沒有被賦值給某個變數,那它的型別就是 NSStackBlock;沒有捕獲外部變數的 block 的類會是 NSGlobalBlock 即不在堆上,也不在棧上,它類似 C 語言函式一樣會在程式碼段中。 Block會強引用是因為是block的結構體中有variables結構體,對捕獲的變數或者地址複製到這個結構體中。並且在block中還有一個BlockDescriptor的結構體,這個結構體中有一個gouxi_helper函式指標,當block在析構時會呼叫這個指標,對強引用物件傳送release訊息 Facebook檢查迴圈引用的庫就是利用dispose指標加上重寫release方法達到檢查強引用的目的
4.CALayer 的subLayer的資料結構以及重繪順序
CALayer在記憶體中是以樹形結構存在的,一個CALayer例項也有一個單獨的superLayer和上面所有的子層(subLayers),它建立了一個有層次結構的層,我們稱之為layer tree,在關於CALayer的文件中我只找到了下面兩種tree
第一份,model tree 就是通過程式碼修改,例如更改layer的屬性等等就在這一份。
第二份,presentation tree這棵樹的內容是當前正被顯示在螢幕上的內容。可以通過對目標檢視呼叫- (id)presentationLayer
獲取當前展示的layer,這個方法的內部實現就是在presentation tree上獲取一份當前顯示的layer的拷貝。
//通過兩個輔助layer在動畫過程中的frame的差值來計算出波浪的最大最小值
CALayer *sideHelperPresentationLayer = (CALayer *)[helperSideView.layer presentationLayer];
CALayer *centerHelperPresentationLayer = (CALayer *)[helperCenterView.layer presentationLayer];
CGRect centerRect = [[centerHelperPresentationLayer valueForKeyPath:@"frame"]CGRectValue];
CGRect sideRect = [[sideHelperPresentationLayer valueForKeyPath:@"frame"]CGRectValue];
diff = sideRect.origin.x - centerRect.origin.x;
[self setNeedsDisplay];
複製程式碼
layer的繪製和UIView是一樣的,都是自低向頂層繪製(由父到子layer)