iOS底層面試題---OC語(yǔ)法部分

面試題的答案都是拋磚引玉,具體細(xì)節(jié)還得深入了解iOS底層原理復(fù)制代碼

1、一個(gè)NSObject對(duì)象占用多少內(nèi)存?

系統(tǒng)分配了16個(gè)字節(jié)給NSObject對(duì)象(通過(guò)malloc_size函數(shù)獲得)

但NSObject對(duì)象內(nèi)部只使用了8個(gè)字節(jié)的空間(64bit環(huán)境下,可以通過(guò)class_getInstanceSize函數(shù)獲得)

2、對(duì)象的isa指針指向哪里?

instance對(duì)象的isa指向class對(duì)象

class對(duì)象的isa指向meta-class對(duì)象

meta-class對(duì)象的isa指向基類(lèi)的meta-class對(duì)象

3、OC的類(lèi)信息存放在哪里?

對(duì)象方法、屬性、成員變量、協(xié)議信息,存放在class對(duì)象中

類(lèi)方法,存放在meta-class對(duì)象中

成員變量的具體值,存放在instance對(duì)象中

4、iOS用什么方式實(shí)現(xiàn)對(duì)一個(gè)對(duì)象的KVO?(KVO的本質(zhì)是什么?)

利用RuntimeAPI動(dòng)態(tài)生成一個(gè)子類(lèi),并且讓instance對(duì)象的isa指向這個(gè)全新的子類(lèi)

當(dāng)修改instance對(duì)象的屬性時(shí),會(huì)調(diào)用Foundation的_NSSetXXXValueAndNotify函數(shù) √ willChangeValueForKey: √ 父類(lèi)原來(lái)的setter √ didChangeValueForKey: √ 內(nèi)部會(huì)觸發(fā)監(jiān)聽(tīng)器(Oberser)的監(jiān)聽(tīng)方法( observeValueForKeyPath:ofObject:change:context:)

5、如何手動(dòng)觸發(fā)KVO?

手動(dòng)調(diào)用willChangeValueForKey:和didChangeValueForKey:

6、直接修改成員變量或?qū)傩詴?huì)觸發(fā)KVO么?

修改成員變量不會(huì)觸發(fā)KVO

修改屬性會(huì)觸發(fā)KVO

7、KVC的賦值和取值過(guò)程是怎樣的?原理是什么?

7.1、KVC賦值

// 1.1 創(chuàng)建人PTLPerson *p = [[PTLPerson alloc] init];self.person = p;// 1.2 創(chuàng)建狗PTLDog *dog = [[PTLDog alloc] init];// 1.3 將狗賦值給人[psetValue:dogforKeyPath:@"dog"];// 1.4 通過(guò)KVC給dog的weight屬性賦值 賦值時(shí)會(huì)自動(dòng)找到人擁有的dog的weight屬性[psetValue:@10.0forKeyPath:@"dog.weight"];NSLog(@"books = %@", [p valueForKeyPath:@"dog.weight"]);[dogprint];復(fù)制代碼

7.2、 KVC取值

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

7.3、 原理

KVO 是 Objective-C 對(duì)觀察者設(shè)計(jì)模式的一種實(shí)現(xiàn),另外一種是:通知機(jī)制(notification) KVO提供一種機(jī)制,指定一個(gè)被觀察對(duì)象(例如A類(lèi)),當(dāng)對(duì)象某個(gè)屬性(例如A中的字符串name)發(fā)生更改時(shí),對(duì)象會(huì)獲得通知,并作出相應(yīng)處理 在MVC設(shè)計(jì)架構(gòu)下的項(xiàng)目,KVO機(jī)制很適合實(shí)現(xiàn)mode模型和controller之間的通訊。 例如:代碼中,在模型類(lèi)A創(chuàng)建屬性數(shù)據(jù),在控制器中創(chuàng)建觀察者,一旦屬性數(shù)據(jù)發(fā)生改變就收到觀察者收到通知,通過(guò)KVO再在控制器使用回調(diào)方法處理實(shí)現(xiàn)視圖B的更新;(本文中的應(yīng)用就是這樣的例子.)

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 的實(shí)現(xiàn)依賴(lài)于 Objective-C 強(qiáng)大的 Runtime【 ,從以上Apple 的文檔可以看出蘋(píng)果對(duì)于KVO機(jī)制的實(shí)現(xiàn)是一筆帶過(guò),而具體的細(xì)節(jié)沒(méi)有過(guò)多的描述,但是我們可以通過(guò)Runtime的所提供的方法去探索關(guān)于KVO機(jī)制的底層實(shí)現(xiàn)原理.

當(dāng)觀察某對(duì)象 A 時(shí),KVO 機(jī)制動(dòng)態(tài)創(chuàng)建一個(gè)對(duì)象A當(dāng)前類(lèi)的子類(lèi),并為這個(gè)新的子類(lèi)重寫(xiě)了被觀察屬性 keyPath 的 setter 方法。setter 方法隨后負(fù)責(zé)通知觀察對(duì)象屬性的改變狀況。

Apple 使用了 isa 混寫(xiě)(isa-swizzling)來(lái)實(shí)現(xiàn) KVO 。當(dāng)觀察對(duì)象A時(shí),KVO機(jī)制動(dòng)態(tài)創(chuàng)建一個(gè)新的名為:NSKVONotifying_A 的新類(lèi),該類(lèi)繼承自對(duì)象A的本類(lèi),且 KVO 為 NSKVONotifying_A 重寫(xiě)觀察屬性的 setter 方法,setter 方法會(huì)負(fù)責(zé)在調(diào)用原 setter 方法之前和之后,通知所有觀察對(duì)象屬性值的更改情況。

NSKVONotifying_A類(lèi)剖析:在這個(gè)過(guò)程,被觀察對(duì)象的 isa 指針從指向原來(lái)的A類(lèi),被KVO機(jī)制修改為指向系統(tǒng)新創(chuàng)建的子類(lèi) NSKVONotifying_A類(lèi),來(lái)實(shí)現(xiàn)當(dāng)前類(lèi)屬性值改變的監(jiān)聽(tīng); 所以當(dāng)我們從應(yīng)用層面上看來(lái),完全沒(méi)有意識(shí)到有新的類(lèi)出現(xiàn),這是系統(tǒng)“隱瞞”了對(duì)KVO的底層實(shí)現(xiàn)過(guò)程,讓我們誤以為還是原來(lái)的類(lèi)。但是此時(shí)如果我們創(chuàng)建一個(gè)新的名為“NSKVONotifying_A”的類(lèi)(),就會(huì)發(fā)現(xiàn)系統(tǒng)運(yùn)行到注冊(cè)KVO的那段代碼時(shí)程序就崩潰,因?yàn)橄到y(tǒng)在注冊(cè)監(jiān)聽(tīng)的時(shí)候動(dòng)態(tài)創(chuàng)建了名為NSKVONotifying_A的中間類(lèi),并指向這個(gè)中間類(lèi)了。 因而在該對(duì)象上對(duì) setter 的調(diào)用就會(huì)調(diào)用已重寫(xiě)的 setter,從而激活鍵值通知機(jī)制。

子類(lèi)setter方法剖析:KVO的鍵值觀察通知依賴(lài)于 NSObject 的兩個(gè)方法:willChangeValueForKey:和 didChangevlueForKey:,在存取數(shù)值的前后分別調(diào)用2個(gè)方法: 被觀察屬性發(fā)生改變之前,willChangeValueForKey:被調(diào)用,通知系統(tǒng)該 keyPath 的屬性值即將變更;當(dāng)改變發(fā)生后, didChangeValueForKey: 被調(diào)用,通知系統(tǒng)該 keyPath 的屬性值已經(jīng)變更; 之后observeValueForKey:ofObject:change:context: 也會(huì)被調(diào)用。且重寫(xiě)觀察屬性的setter 方法這種繼承方式的注入是在運(yùn)行時(shí)而不是編譯時(shí)實(shí)現(xiàn)的。 KVO為子類(lèi)的觀察者屬性重寫(xiě)調(diào)用存取方法的工作原理在代碼中相當(dāng)于:

-(void)setName:(NSString *)newName { [self willChangeValueForKey:@"name"]; //KVO在調(diào)用存取方法之前總調(diào)用 [supersetValue:newNameforKey:@"name"]; //調(diào)用父類(lèi)的存取方法 [self didChangeValueForKey:@"name"]; //KVO在調(diào)用存取方法之后總調(diào)用 }復(fù)制代碼

8、Category的實(shí)現(xiàn)原理?

Category編譯之后的底層結(jié)構(gòu)是struct category_t,里面存儲(chǔ)著分類(lèi)的對(duì)象方法、類(lèi)方法、屬性、協(xié)議信息

在程序運(yùn)行的時(shí)候,runtime會(huì)將Category的數(shù)據(jù),合并到類(lèi)信息中(類(lèi)對(duì)象、元類(lèi)對(duì)象中)

9、Category和Class Extension的區(qū)別是什么?

Class Extension在編譯的時(shí)候,它的數(shù)據(jù)就已經(jīng)包含在類(lèi)信息中

Category是在運(yùn)行時(shí),才會(huì)將數(shù)據(jù)合并到類(lèi)信息中

10、Category中有l(wèi)oad方法嗎?load方法是什么時(shí)候調(diào)用的?load 方法能繼承嗎?

有l(wèi)oad方法

load方法在runtime加載類(lèi)、分類(lèi)的時(shí)候調(diào)用

load方法可以繼承,但是一般情況下不會(huì)主動(dòng)去調(diào)用load方法,都是讓系統(tǒng)自動(dòng)調(diào)用

11、+load、+initialize方法的區(qū)別什么?它們?cè)赾ategory中的調(diào)用的順序?以及出現(xiàn)繼承時(shí)他們之間的調(diào)用過(guò)程?

+load

+load方法會(huì)在runtime加載類(lèi)、分類(lèi)時(shí)調(diào)用

每個(gè)類(lèi)、分類(lèi)的+load,在程序運(yùn)行過(guò)程中只調(diào)用一次

調(diào)用順序:

1、先調(diào)用類(lèi)的+load √ 按照編譯先后順序調(diào)用(先編譯,先調(diào)用) √ 調(diào)用子類(lèi)的+load之前會(huì)先調(diào)用父類(lèi)的+load

2、再調(diào)用分類(lèi)的+load √ 按照編譯先后順序調(diào)用(先編譯,先調(diào)用)

+initialize

+initialize方法會(huì)在類(lèi)第一次接收到消息時(shí)調(diào)用

調(diào)用順序 1、先調(diào)用父類(lèi)的+initialize,再調(diào)用子類(lèi)的+initialize 2、(先初始化父類(lèi),再初始化子類(lèi),每個(gè)類(lèi)只會(huì)初始化1次)

+initialize和+load的很大區(qū)別是,+initialize是通過(guò)objc_msgSend進(jìn)行調(diào)用的,所以有以下特點(diǎn) √ 如果子類(lèi)沒(méi)有實(shí)現(xiàn)+initialize,會(huì)調(diào)用父類(lèi)的+initialize(所以父類(lèi)的+initialize可能會(huì)被調(diào)用多次) √ 如果分類(lèi)實(shí)現(xiàn)了+initialize,就覆蓋類(lèi)本身的+initialize調(diào)用

12、Category能否添加成員變量?如果可以,如何給Category添加成員變量?

默認(rèn)情況下,因?yàn)榉诸?lèi)底層結(jié)構(gòu)的限制,不能添加成員變量到分類(lèi)中。但可以通過(guò)關(guān)聯(lián)對(duì)象來(lái)間接實(shí)現(xiàn)

13、block的原理是怎樣的?本質(zhì)是什么?

block本質(zhì)上也是一個(gè)OC對(duì)象,它內(nèi)部也有個(gè)isa指針

封裝了函數(shù)調(diào)用以及調(diào)用環(huán)境的OC對(duì)象

14、__block的作用是什么?

__block說(shuō)明符類(lèi)似static、auto、register一樣,只要觀察到該變量被 block 所持有,就將“外部變量”在棧中的內(nèi)存地址放到了堆中。 進(jìn)而在block內(nèi)部也可以修改外部變量的值。

__block可以用于解決block內(nèi)部無(wú)法修改auto變量值的問(wèn)題

__block不能修飾全局變量、靜態(tài)變量(static)

編譯器會(huì)將__block變量包裝成一個(gè)對(duì)象

15、block的屬性修飾詞為什么是copy?使用block有哪些使用注意?

block一旦沒(méi)有進(jìn)行copy操作,就不會(huì)在堆上

使用注意:循環(huán)引用問(wèn)題

16、block在修改NSMutableArray,需不需要添加__block?

不需要

當(dāng)變量是一個(gè)指針的時(shí)候,block里只是復(fù)制了一份這個(gè)指針,兩個(gè)指針指向同一個(gè)地址。所以,在block里面對(duì)指針指向內(nèi)容做的修改,在block外面也一樣生效。

17、說(shuō)說(shuō)isa指針?

instance的isa指向class,當(dāng)調(diào)用對(duì)象方法時(shí),通過(guò)instance的isa找到class,最后找到對(duì)象方法的實(shí)現(xiàn)進(jìn)行調(diào)用

class的isa指向meta-class,當(dāng)調(diào)用類(lèi)方法時(shí),通過(guò)class的isa找到meta-class,最后找到類(lèi)方法的實(shí)現(xiàn)進(jìn)行調(diào)用

當(dāng)Student的instance對(duì)象要調(diào)用Person的對(duì)象方法時(shí)(Student繼承自Person),會(huì)先通過(guò)isa找到Student的class,然后通過(guò)superclass找到Person的class,最后找到對(duì)象方法的實(shí)現(xiàn)進(jìn)行調(diào)用

當(dāng)Student的class要調(diào)用Person的類(lèi)方法時(shí)(Student繼承自Person),會(huì)先通過(guò)isa找到Student的meta-class,然后通過(guò)superclass找到Person的meta-class,最后找到類(lèi)方法的實(shí)現(xiàn)進(jìn)行調(diào)用

在arm64架構(gòu)之前,isa就是一個(gè)普通的指針,存儲(chǔ)著Class、Meta-Class對(duì)象的內(nèi)存地址

從arm64架構(gòu)開(kāi)始,對(duì)isa進(jìn)行了優(yōu)化,變成了一個(gè)共用體(union)結(jié)構(gòu),還使用位域來(lái)存儲(chǔ)更多的信息

從64bit開(kāi)始,isa需要進(jìn)行一次位運(yùn)算,才能計(jì)算出真實(shí)地址

18、isa、superclass總結(jié)

instance的isa指向class

class的isa指向meta-class

meta-class的isa指向基類(lèi)的meta-class

class的superclass指向父類(lèi)的class 如果沒(méi)有父類(lèi),superclass指針為nil

meta-class的superclass指向父類(lèi)的meta-class 基類(lèi)的meta-class的superclass指向基類(lèi)的class

instance調(diào)用對(duì)象方法的軌跡 isa找到class,方法不存在,就通過(guò)superclass找父類(lèi)

class調(diào)用類(lèi)方法的軌跡 isa找meta-class,方法不存在,就通過(guò)superclass找父類(lèi)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、一個(gè)NSObject對(duì)象占用多少內(nèi)存? 系統(tǒng)分配了16個(gè)字節(jié)給NSObject對(duì)象(通過(guò)malloc_size...
    ptlCoder閱讀 731評(píng)論 0 5
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,030評(píng)論 0 9
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,621評(píng)論 1 32
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 30,192評(píng)論 8 265
  • 期末了沒(méi)什么壓力,準(zhǔn)備把宮崎駿大師的動(dòng)漫都看一遍。 剛看完千與千尋,我不是孩子卻想以一個(gè)孩子的視角成年人的感悟去看...
    羅小扇閱讀 789評(píng)論 0 1

友情鏈接更多精彩內(nèi)容