iOS底層面試題---OC語法部分

面試題的答案都是拋磚引玉,具體細節還得深刻了解iOS底層原理
複製代碼

一、一個NSObject對象佔用多少內存?

  • 系統分配了16個字節給NSObject對象(經過malloc_size函數得到)
  • 但NSObject對象內部只使用了8個字節的空間(64bit環境下,能夠經過class_getInstanceSize函數得到)

二、對象的isa指針指向哪裏?

  • instance對象的isa指向class對象
  • class對象的isa指向meta-class對象
  • meta-class對象的isa指向基類的meta-class對象

三、OC的類信息存放在哪裏?

  • 對象方法、屬性、成員變量、協議信息,存放在class對象中
  • 類方法,存放在meta-class對象中
  • 成員變量的具體值,存放在instance對象中

四、iOS用什麼方式實現對一個對象的KVO?(KVO的本質是什麼?)

  • 利用RuntimeAPI動態生成一個子類,而且讓instance對象的isa指向這個全新的子類
  • 當修改instance對象的屬性時,會調用Foundation的_NSSetXXXValueAndNotify函數 √ willChangeValueForKey: √ 父類原來的setter √ didChangeValueForKey: √ 內部會觸發監聽器(Oberser)的監聽方法( observeValueForKeyPath:ofObject:change:context:)

五、如何手動觸發KVO?

  • 手動調用willChangeValueForKey:和didChangeValueForKey:

六、直接修改爲員變量或屬性會觸發KVO麼?

  • 修改爲員變量不會觸發KVO
  • 修改屬性會觸發KVO

七、KVC的賦值和取值過程是怎樣的?原理是什麼?

7.一、KVC賦值面試

// 1.1 建立人
PTLPerson *p = [[PTLPerson alloc] init];
self.person = p;
// 1.2 建立狗
PTLDog *dog = [[PTLDog alloc] init];
// 1.3 將狗賦值給人
[p setValue:dog forKeyPath:@"dog"];
// 1.4 經過KVC給dog的weight屬性賦值 
賦值時會自動找到人擁有的dog的weight屬性
[p setValue:@10.0 forKeyPath:@"dog.weight"];
NSLog(@"books = %@", [p valueForKeyPath:@"dog.weight"]);
[dog print];
複製代碼

7.二、 KVC取值設計模式

NSMutableArray *tempM = [NSMutableArray array];
// 2.1 kvc取出出數組books中price的值
for (PTLBook *book in [p valueForKeyPath:@"books"]) {
    [tempM addObject:[book valueForKeyPath:@"price"]];
}
NSLog(@"%@", tempM);
// 2.2 kvc取出數組中price的最大值
NSLog(@"Max = %@", [[p valueForKeyPath:@"books"] valueForKeyPath:@"@max.price"]);
複製代碼

7.三、 原理數組

  • KVO 是 Objective-C 對觀察者設計模式的一種實現,另一種是:通知機制(notification) KVO提供一種機制,指定一個被觀察對象(例如A類),當對象某個屬性(例如A中的字符串name)發生更改時,對象會得到通知,並做出相應處理 在MVC設計架構下的項目,KVO機制很適合實現mode模型和controller之間的通信。 例如:代碼中,在模型類A建立屬性數據,在控制器中建立觀察者,一旦屬性數據發生改變就收到觀察者收到通知,經過KVO再在控制器使用回調方法處理實現視圖B的更新;(本文中的應用就是這樣的例子.)bash

  • KVO在Apple中的API文檔以下: Automatic key-value observing is implemented using a technique called isa-swizzling… When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class … KVO 的實現依賴於 Objective-C 強大的 Runtime【 ,從以上Apple 的文檔能夠看出蘋果對於KVO機制的實現是一筆帶過,而具體的細節沒有過多的描述,可是咱們能夠經過Runtime的所提供的方法去探索關於KVO機制的底層實現原理.架構

  • 當觀察某對象 A 時,KVO 機制動態建立一個對象A當前類的子類,併爲這個新的子類重寫了被觀察屬性 keyPath 的 setter 方法。setter 方法隨後負責通知觀察對象屬性的改變情況。函數

  • Apple 使用了 isa 混寫(isa-swizzling)來實現 KVO 。當觀察對象A時,KVO機制動態建立一個新的名爲:NSKVONotifying_A 的新類,該類繼承自對象A的本類,且 KVO 爲 NSKVONotifying_A 重寫觀察屬性的 setter 方法,setter 方法會負責在調用原 setter 方法以前和以後,通知全部觀察對象屬性值的更改狀況。優化

  • NSKVONotifying_A類剖析:在這個過程,被觀察對象的 isa 指針從指向原來的A類,被KVO機制修改成指向系統新建立的子類 NSKVONotifying_A類,來實現當前類屬性值改變的監聽; 因此當咱們從應用層面上看來,徹底沒有意識到有新的類出現,這是系統「隱瞞」了對KVO的底層實現過程,讓咱們誤覺得仍是原來的類。可是此時若是咱們建立一個新的名爲「NSKVONotifying_A」的類(),就會發現系統運行到註冊KVO的那段代碼時程序就崩潰,由於系統在註冊監聽的時候動態建立了名爲NSKVONotifying_A的中間類,並指向這個中間類了。 於是在該對象上對 setter 的調用就會調用已重寫的 setter,從而激活鍵值通知機制。ui

  • 子類setter方法剖析:KVO的鍵值觀察通知依賴於 NSObject 的兩個方法:willChangeValueForKey:和 didChangevlueForKey:,在存取數值的先後分別調用2個方法: 被觀察屬性發生改變以前,willChangeValueForKey:被調用,通知系統該 keyPath 的屬性值即將變動;當改變發生後, didChangeValueForKey: 被調用,通知系統該 keyPath 的屬性值已經變動; 以後observeValueForKey:ofObject:change:context: 也會被調用。且重寫觀察屬性的setter 方法這種繼承方式的注入是在運行時而不是編譯時實現的。 KVO爲子類的觀察者屬性重寫調用存取方法的工做原理在代碼中至關於:spa

-(void)setName:(NSString *)newName 
{ 
[self willChangeValueForKey:@"name"]; //KVO在調用存取方法以前總調用 
[super setValue:newName forKey:@"name"]; //調用父類的存取方法 
[self didChangeValueForKey:@"name"]; //KVO在調用存取方法以後總調用 
} 
複製代碼

八、Category的實現原理?

  • Category編譯以後的底層結構是struct category_t,裏面存儲着分類的對象方法、類方法、屬性、協議信息
  • 在程序運行的時候,runtime會將Category的數據,合併到類信息中(類對象、元類對象中)

九、Category和Class Extension的區別是什麼?

  • Class Extension在編譯的時候,它的數據就已經包含在類信息中
  • Category是在運行時,纔會將數據合併到類信息中

十、Category中有load方法嗎?load方法是何時調用的?load 方法能繼承嗎?

  • 有load方法
  • load方法在runtime加載類、分類的時候調用
  • load方法能夠繼承,可是通常狀況下不會主動去調用load方法,都是讓系統自動調用

十一、+load、+initialize方法的區別什麼?它們在category中的調用的順序?以及出現繼承時他們之間的調用過程?

+load
  • +load方法會在runtime加載類、分類時調用
  • 每一個類、分類的+load,在程序運行過程當中只調用一次
  • 調用順序:
  • 一、先調用類的+load √ 按照編譯前後順序調用(先編譯,先調用) √ 調用子類的+load以前會先調用父類的+load
  • 二、再調用分類的+load √ 按照編譯前後順序調用(先編譯,先調用)
+initialize
  • +initialize方法會在類第一次接收到消息時調用
  • 調用順序 一、先調用父類的+initialize,再調用子類的+initialize 二、(先初始化父類,再初始化子類,每一個類只會初始化1次)
  • +initialize和+load的很大區別是,+initialize是經過objc_msgSend進行調用的,因此有如下特色 √ 若是子類沒有實現+initialize,會調用父類的+initialize(因此父類的+initialize可能會被調用屢次) √ 若是分類實現了+initialize,就覆蓋類自己的+initialize調用

十二、Category可否添加成員變量?若是能夠,如何給Category添加成員變量?

  • 默認狀況下,由於分類底層結構的限制,不能添加成員變量到分類中。但能夠經過關聯對象來間接實現

1三、block的原理是怎樣的?本質是什麼?

  • block本質上也是一個OC對象,它內部也有個isa指針
  • 封裝了函數調用以及調用環境的OC對象

1四、__block的做用是什麼?

  • __block說明符相似static、auto、register同樣,只要觀察到該變量被 block 所持有,就將「外部變量」在棧中的內存地址放到了堆中。 進而在block內部也能夠修改外部變量的值。
  • __block能夠用於解決block內部沒法修改auto變量值的問題
  • __block不能修飾全局變量、靜態變量(static)
  • 編譯器會將__block變量包裝成一個對象

1五、block的屬性修飾詞爲何是copy?使用block有哪些使用注意?

  • block一旦沒有進行copy操做,就不會在堆上
  • 使用注意:循環引用問題

1六、block在修改NSMutableArray,需不須要添加__block?

  • 不須要
  • 當變量是一個指針的時候,block裏只是複製了一份這個指針,兩個指針指向同一個地址。因此,在block裏面對指針指向內容作的修改,在block外面也同樣生效。

1七、說說isa指針?

  • instance的isa指向class,當調用對象方法時,經過instance的isa找到class,最後找到對象方法的實現進行調用
  • class的isa指向meta-class,當調用類方法時,經過class的isa找到meta-class,最後找到類方法的實現進行調用
  • 當Student的instance對象要調用Person的對象方法時(Student繼承自Person),會先經過isa找到Student的class,而後經過superclass找到Person的class,最後找到對象方法的實現進行調用
  • 當Student的class要調用Person的類方法時(Student繼承自Person),會先經過isa找到Student的meta-class,而後經過superclass找到Person的meta-class,最後找到類方法的實現進行調用
  • 在arm64架構以前,isa就是一個普通的指針,存儲着Class、Meta-Class對象的內存地址
  • 從arm64架構開始,對isa進行了優化,變成了一個共用體(union)結構,還使用位域來存儲更多的信息
  • 從64bit開始,isa須要進行一次位運算,才能計算出真實地址

1八、isa、superclass總結

  • instance的isa指向class
  • class的isa指向meta-class
  • meta-class的isa指向基類的meta-class
  • class的superclass指向父類的class 若是沒有父類,superclass指針爲nil
  • meta-class的superclass指向父類的meta-class 基類的meta-class的superclass指向基類的class
  • instance調用對象方法的軌跡 isa找到class,方法不存在,就經過superclass找父類
  • class調用類方法的軌跡 isa找meta-class,方法不存在,就經過superclass找父類
相關文章
相關標籤/搜索