一、OC語(yǔ)法
1、OC對(duì)象的本質(zhì)
1)一個(gè)NSObject對(duì)象占用多少內(nèi)存?
A:系統(tǒng)分配16個(gè)字節(jié)給一個(gè)NSObject對(duì)象(可以通過(guò)C函數(shù)malloc_size函數(shù)獲得,通過(guò)查看OC源碼,alloc函數(shù),也即allocWithZone:中有判斷當(dāng)字節(jié)數(shù)小于16時(shí)就分配16個(gè)字節(jié));而一個(gè)NSObject對(duì)象僅使用8個(gè)字節(jié),用于存放成員變量isa指針,(可以通過(guò)runtime 的函數(shù)class_getInstanceSize獲得)
………………..
2)對(duì)象的isa指針指向哪里?
instance對(duì)象的isa指向class對(duì)象
class對(duì)象的isa指向meta-class對(duì)象
meta-class對(duì)象指向基類(lèi)的meta-class對(duì)象
(以上的“指向”實(shí)際是通過(guò)isa與ISA_MASK常量進(jìn)行&位運(yùn)算的結(jié)果)
3)OC的類(lèi)信息存放在哪里?
成員變量、屬性、協(xié)議、實(shí)例方法等信息存放在類(lèi)對(duì)象中;
類(lèi)方法存放在元類(lèi)對(duì)象中;
另外,成員變量的具體值存放在實(shí)例對(duì)象中。
2、KVO
1)iOS用什么方式實(shí)現(xiàn)對(duì)一個(gè)對(duì)象的KVO?(KVO的本質(zhì)是什么?)
當(dāng)一個(gè)對(duì)象的屬性被監(jiān)聽(tīng)時(shí),runtime會(huì)動(dòng)態(tài)創(chuàng)建一個(gè)新的類(lèi),這個(gè)新的類(lèi)是原有類(lèi)的子類(lèi)(NSKVONotifying_原有類(lèi)名),對(duì)象的isa會(huì)指向這個(gè)新類(lèi)的類(lèi)對(duì)象Class。
當(dāng)修改對(duì)象的屬性時(shí),會(huì)調(diào)用 Foundation 中的 _NSSetXXValueAndNotify 函數(shù),
這個(gè)函數(shù)會(huì)調(diào)用willChangeValueForKey:, 然后調(diào)用原有類(lèi)的setter以修改屬性值,最后調(diào)用didChangeValueForKey:, didChangeValueForKey:內(nèi)部會(huì)觸發(fā)監(jiān)聽(tīng)器(Observer)的監(jiān)聽(tīng)方法-observeValueForKeyPath:ofObject:change:contex:
2)如何手動(dòng)觸發(fā)KVO?
手動(dòng)調(diào)用被監(jiān)聽(tīng)對(duì)象的willChangeValueForKey:方法,讓后調(diào)用didChangeValueForKey:方法,兩個(gè)方法都需要調(diào)用,否則不會(huì)觸發(fā)KVO
3)直接修改成員變量會(huì)觸發(fā)KVO嗎?
不會(huì)。因?yàn)椴粫?huì)走setter方法,就不會(huì)走觸發(fā)KVO的流程
3、KVC
1)通過(guò)KVC修改屬性會(huì)觸發(fā)KVO么?
會(huì)觸發(fā)KVO,因?yàn)?setValueForKey:方法會(huì)觸發(fā)willChangeValueForKey: 和 didChangeValueForKey:方法
2)KVC的賦值和取值時(shí)怎樣的?原理是什么?
賦值:調(diào)用setValue:forkey:方法時(shí),會(huì)按 -setKey:,-_setKey: 的順序查找對(duì)象的方法,若查到有實(shí)現(xiàn)其中的方法,則調(diào)用;若都沒(méi)有實(shí)現(xiàn),則判斷 +accessInstanceVariableDirectly 方法的返回值,若為YES,則按 _key, _isKey, key, isKey的順序查到對(duì)象的實(shí)例變量,查找到則賦值。若方法沒(méi)有實(shí)現(xiàn),+accessInstanceVariableDirectly返回NO或者實(shí)例變量都不存在,則會(huì)調(diào)用valueForUndefinedKey:,并報(bào) NSUnknownKeyException 錯(cuò)誤。
取值:調(diào)用valueForKey: 會(huì)按 -getKey, -key, -isKey, -_key的順序查找方法,若查到有實(shí)現(xiàn)其中的方法,則調(diào)用并返回值;若都沒(méi)有實(shí)現(xiàn),則判斷 +accessInstanceVariableDirectly 方法的返回值,若為YES,則按 _key, _isKey, key, isKey的順序查找成員變量,查找到則將成員變量的值返回。若方法沒(méi)有實(shí)現(xiàn),+accessInstanceVariableDirectly返回NO或者實(shí)例變量都不存在,則會(huì)調(diào)用valueForUndefinedKey:,并報(bào) NSUnknownKeyException 錯(cuò)誤。
4、Category
1)Category的使用場(chǎng)合是什么?
給已有的類(lèi)擴(kuò)展屬性、方法、和協(xié)議;
將一個(gè)類(lèi)按照功能拆分為不同的分類(lèi),方便管理和閱讀代碼
2)Category的實(shí)現(xiàn)原理
Category在編譯完成后的底層結(jié)構(gòu)是 struct category_t 的結(jié)構(gòu)體,結(jié)構(gòu)體中存放著Category數(shù)據(jù)(類(lèi)名,屬性列表,對(duì)象方法列表,類(lèi)方法列表,協(xié)議列表)
在程序運(yùn)行的時(shí)候,通過(guò)runtime將Category數(shù)據(jù)合并到原類(lèi)的類(lèi)對(duì)象和原類(lèi)對(duì)象中()
4)Category和Class Extension的區(qū)別是什么?
Category是在運(yùn)行時(shí)將其中的數(shù)據(jù)(如方法等)合并到原類(lèi)中,
而Class Extension是在編譯時(shí)就將數(shù)據(jù)合并到原類(lèi)中
5)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)用
6)load、initialize方法的區(qū)別什么?它們?cè)赾ategory中的調(diào)用的順序?以及出現(xiàn)繼承時(shí)他們之間的調(diào)用過(guò)程?
①區(qū)別:
調(diào)用時(shí)刻:
load是runtime加載類(lèi)、分類(lèi)時(shí)調(diào)用(只會(huì)調(diào)用1次);
initialize時(shí)類(lèi)第1次接受到消息時(shí)調(diào)用,每個(gè)類(lèi)只會(huì)initialize一次(但是子類(lèi)沒(méi)有實(shí)現(xiàn)initialize方法,則父類(lèi)的initialize方法可能會(huì)調(diào)用多次)
調(diào)用方式:
load時(shí)根據(jù)函數(shù)地址直接調(diào)用;而initialize時(shí)通過(guò)objc_msgSend調(diào)用
②調(diào)用順序
load : a.先調(diào)用類(lèi)的load:先編譯的類(lèi)優(yōu)先調(diào)用load;調(diào)用子類(lèi)的load之前,會(huì)先調(diào)用父類(lèi)的load
b.后調(diào)用分類(lèi)的load:先編譯的分類(lèi)優(yōu)先調(diào)用
initialize: a.先初始化父類(lèi)調(diào)用父類(lèi)initialize
b.再初始化子類(lèi),調(diào)用子類(lèi)的initialize(若子類(lèi)沒(méi)有實(shí)現(xiàn)initialize方法,則最終調(diào)用的的是父類(lèi)的intialize方法)
5、Block
1)block 的原理是怎樣的?本質(zhì)是什么?
block 是封裝了函數(shù)調(diào)用和調(diào)用環(huán)境的 OC 對(duì)象
2)__block 的作用是什么?有什么使用注意點(diǎn)?
作用:被 __block 修飾的 auto 變量(即非全局變量、非 static 變量的局部變量),會(huì)被包裝成一個(gè) OC 對(duì)象(結(jié)構(gòu)體),變量的實(shí)際值會(huì)作為該結(jié)構(gòu)體的一個(gè)成員,這樣可以解決在 block 中無(wú)法修改 auto 變量的問(wèn)題。
注意點(diǎn):內(nèi)存管理相關(guān),__block 修飾的變量在棧上時(shí),block 不會(huì)對(duì)其強(qiáng)引用;__block 變量被 copy 到堆上時(shí),在 MRC 下,block 不會(huì)對(duì)其強(qiáng)引用,在 ARC 下,會(huì)根據(jù)變量的其他修飾關(guān)鍵字進(jìn)行強(qiáng)引用或弱引用。
3)block的屬性修飾詞為什么是 copy?使用block有哪些使用注意?
為什么是 copy:block 沒(méi)有被 copy 之前,是在內(nèi)存中的棧上或者全局區(qū),生命周期由系統(tǒng)管理;而 copy 之后會(huì)被 copy 到堆上,此時(shí) block 生命周期由代碼管理。
使用注意:循環(huán)引用問(wèn)題,會(huì)導(dǎo)致內(nèi)存泄漏。
4)block 在修改 NSMutableArray (添加或移除元素),需不需要添加 __block?
不需要。因?yàn)閷?duì) NSMutableArray 添加或移除元素不需要修改變量的值。
Runtime
1、消息機(jī)制
1)講一下 OC 的消息機(jī)制
OC 對(duì)象調(diào)用方法底層轉(zhuǎn)為 objc_msgSend 函數(shù)調(diào)用,即給對(duì)象(消息接受者)發(fā)送一條消息(selector 方法名);
objc_msgSend 函數(shù)底層實(shí)現(xiàn)大致分為三大階段:消息發(fā)送,動(dòng)態(tài)方法解析,消息轉(zhuǎn)發(fā);
消息發(fā)送:通過(guò)對(duì)象的 isa 指針找到類(lèi)對(duì)象或元類(lèi)對(duì)象(取決于消息接受者是實(shí)例對(duì)象還是類(lèi)對(duì)象,這里假設(shè)是實(shí)例對(duì)象,類(lèi)對(duì)象的處理類(lèi)似),依次在類(lèi)對(duì)象中的方法緩存列表和方法列表中查找消息的 selector 方法名,若找到則直接調(diào)用方法,否則繼續(xù)從父類(lèi)的方法列表中查找,一直找到基類(lèi)為止;若查找到基類(lèi)也沒(méi)有找到方法,則進(jìn)入動(dòng)態(tài)方法解析階段;
動(dòng)態(tài)方法解析:實(shí)現(xiàn) -resolveInstanceMethod:(SEL)sel 方法,并調(diào)用 runtime 的方法 class_addMethod,為對(duì)象動(dòng)態(tài)地添加一個(gè)方法,然后再次進(jìn)入消息發(fā)送階段,并標(biāo)記已經(jīng)嘗試過(guò)動(dòng)態(tài)方法解析;若添加了方法,則直接調(diào)用;若沒(méi)有添加,則進(jìn)入消息轉(zhuǎn)發(fā)階段;
消息轉(zhuǎn)發(fā):實(shí)現(xiàn) -forwardingTargetForSelector: 方法,返回一個(gè)新的對(duì)象做為消息接受者,并調(diào)用 objc_msgSend 方法;若沒(méi)有返回新的對(duì)象而是返回 nil,則會(huì)調(diào)用 -methodSignatureForSelctor: 方法,若此方法返回一個(gè)方法簽名,則進(jìn)入 -forwardInvocation: 方法,可以做任何處理;若返回 nil,則會(huì)報(bào)經(jīng)典錯(cuò)誤 unrecognized selector sent to instance xxxxx
2)消息轉(zhuǎn)發(fā)機(jī)制流程
同問(wèn)題1)
3)什么是Runtime?平時(shí)項(xiàng)目中有用過(guò)么?
Runtime:OC 是一門(mén)動(dòng)態(tài)性很強(qiáng)的語(yǔ)言,很多操作允許推遲到程序運(yùn)行時(shí)再執(zhí)行;
OC 的動(dòng)態(tài)性是由 Runtime 來(lái)實(shí)現(xiàn)的,Runtime 是一套 C 語(yǔ)言的 API,封裝了很多動(dòng)態(tài)性相關(guān)的函數(shù);
平時(shí)編寫(xiě)的 OC 代碼,底層都是轉(zhuǎn)換為了 Runtime 的 API 進(jìn)行調(diào)用。
用途:給分類(lèi)添加屬性實(shí)現(xiàn)(關(guān)聯(lián)對(duì)象 AssociateObject);
方法交換(method_exchange);
遍歷類(lèi)的所有成員變量(歸檔解檔,獲取私有成員如textField._placeholderLabel,字典轉(zhuǎn)模型)
利用消息轉(zhuǎn)發(fā)機(jī)制,監(jiān)測(cè)方法找不到的問(wèn)題;
...