一驯用、OC語法
1没咙、OC對象的本質(zhì)
1)一個(gè)NSObject對象占用多少內(nèi)存立膛?
A:系統(tǒng)分配16個(gè)字節(jié)給一個(gè)NSObject對象(可以通過C函數(shù)malloc_size函數(shù)獲得,通過查看OC源碼铁孵,alloc函數(shù)锭硼,也即allocWithZone:中有判斷當(dāng)字節(jié)數(shù)小于16時(shí)就分配16個(gè)字節(jié));而一個(gè)NSObject對象僅使用8個(gè)字節(jié)蜕劝,用于存放成員變量isa指針檀头,(可以通過runtime 的函數(shù)class_getInstanceSize獲得)
………………..
2)對象的isa指針指向哪里?
instance對象的isa指向class對象
class對象的isa指向meta-class對象
meta-class對象指向基類的meta-class對象
(以上的“指向”實(shí)際是通過isa與ISA_MASK常量進(jìn)行&位運(yùn)算的結(jié)果)
3)OC的類信息存放在哪里岖沛?
成員變量暑始、屬性、協(xié)議婴削、實(shí)例方法等信息存放在類對象中廊镜;
類方法存放在元類對象中;
另外唉俗,成員變量的具體值存放在實(shí)例對象中嗤朴。
2、KVO
1)iOS用什么方式實(shí)現(xiàn)對一個(gè)對象的KVO虫溜?(KVO的本質(zhì)是什么雹姊?)
當(dāng)一個(gè)對象的屬性被監(jiān)聽時(shí),runtime會(huì)動(dòng)態(tài)創(chuàng)建一個(gè)新的類衡楞,這個(gè)新的類是原有類的子類(NSKVONotifying_原有類名)吱雏,對象的isa會(huì)指向這個(gè)新類的類對象Class。
當(dāng)修改對象的屬性時(shí),會(huì)調(diào)用 Foundation 中的 _NSSetXXValueAndNotify 函數(shù)歧杏,
這個(gè)函數(shù)會(huì)調(diào)用willChangeValueForKey:镰惦, 然后調(diào)用原有類的setter以修改屬性值,最后調(diào)用didChangeValueForKey:, didChangeValueForKey:內(nèi)部會(huì)觸發(fā)監(jiān)聽器(Observer)的監(jiān)聽方法-observeValueForKeyPath:ofObject:change:contex:
2)如何手動(dòng)觸發(fā)KVO犬绒?
手動(dòng)調(diào)用被監(jiān)聽對象的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)通過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: 的順序查找對象的方法餐禁,若查到有實(shí)現(xiàn)其中的方法血久,則調(diào)用;若都沒有實(shí)現(xiàn)帮非,則判斷 +accessInstanceVariableDirectly 方法的返回值氧吐,若為YES,則按 _key, _isKey, key, isKey的順序查到對象的實(shí)例變量末盔,查找到則賦值筑舅。若方法沒有實(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)用并返回值误墓;若都沒有實(shí)現(xiàn),則判斷 +accessInstanceVariableDirectly 方法的返回值益缎,若為YES谜慌,則按 _key, _isKey, key, isKey的順序查找成員變量,查找到則將成員變量的值返回链峭。若方法沒有實(shí)現(xiàn)畦娄,+accessInstanceVariableDirectly返回NO或者實(shí)例變量都不存在,則會(huì)調(diào)用valueForUndefinedKey:,并報(bào) NSUnknownKeyException 錯(cuò)誤。
4熙卡、Category
1)Category的使用場合是什么杖刷?
給已有的類擴(kuò)展屬性、方法驳癌、和協(xié)議滑燃;
將一個(gè)類按照功能拆分為不同的分類,方便管理和閱讀代碼
2)Category的實(shí)現(xiàn)原理
Category在編譯完成后的底層結(jié)構(gòu)是 struct category_t 的結(jié)構(gòu)體颓鲜,結(jié)構(gòu)體中存放著Category數(shù)據(jù)(類名表窘,屬性列表,對象方法列表甜滨,類方法列表乐严,協(xié)議列表)
在程序運(yùn)行的時(shí)候,通過runtime將Category數(shù)據(jù)合并到原類的類對象和原類對象中()
4)Category和Class Extension的區(qū)別是什么衣摩?
Category是在運(yùn)行時(shí)將其中的數(shù)據(jù)(如方法等)合并到原類中昂验,
而Class Extension是在編譯時(shí)就將數(shù)據(jù)合并到原類中
5)Category中有l(wèi)oad方法嗎?load方法是什么時(shí)候調(diào)用的艾扮?load 方法能繼承嗎既琴?
·有l(wèi)oad方法
·load方法是在runtime加載類、分類時(shí)調(diào)用的
·load方法可以繼承泡嘴,但一般不會(huì)主動(dòng)調(diào)用load方法甫恩,而是系統(tǒng)自動(dòng)調(diào)用
6)load、initialize方法的區(qū)別什么酌予?它們在category中的調(diào)用的順序磺箕?以及出現(xiàn)繼承時(shí)他們之間的調(diào)用過程?
①區(qū)別:
調(diào)用時(shí)刻:
load是runtime加載類抛虫、分類時(shí)調(diào)用(只會(huì)調(diào)用1次)滞磺;
initialize時(shí)類第1次接受到消息時(shí)調(diào)用,每個(gè)類只會(huì)initialize一次(但是子類沒有實(shí)現(xiàn)initialize方法莱褒,則父類的initialize方法可能會(huì)調(diào)用多次)
調(diào)用方式:
load時(shí)根據(jù)函數(shù)地址直接調(diào)用击困;而initialize時(shí)通過objc_msgSend調(diào)用
②調(diào)用順序
load : a.先調(diào)用類的load:先編譯的類優(yōu)先調(diào)用load;調(diào)用子類的load之前广凸,會(huì)先調(diào)用父類的load
b.后調(diào)用分類的load:先編譯的分類優(yōu)先調(diào)用
initialize: a.先初始化父類調(diào)用父類initialize
b.再初始化子類阅茶,調(diào)用子類的initialize(若子類沒有實(shí)現(xiàn)initialize方法,則最終調(diào)用的的是父類的intialize方法)
5谅海、Block
1)block 的原理是怎樣的脸哀?本質(zhì)是什么?
block 是封裝了函數(shù)調(diào)用和調(diào)用環(huán)境的 OC 對象
2)__block 的作用是什么扭吁?有什么使用注意點(diǎn)撞蜂?
作用:被 __block 修飾的 auto 變量(即非全局變量盲镶、非 static 變量的局部變量),會(huì)被包裝成一個(gè) OC 對象(結(jié)構(gòu)體)蝌诡,變量的實(shí)際值會(huì)作為該結(jié)構(gòu)體的一個(gè)成員溉贿,這樣可以解決在 block 中無法修改 auto 變量的問題。
注意點(diǎn):內(nèi)存管理相關(guān)浦旱,__block 修飾的變量在棧上時(shí)宇色,block 不會(huì)對其強(qiáng)引用;__block 變量被 copy 到堆上時(shí)颁湖,在 MRC 下宣蠕,block 不會(huì)對其強(qiáng)引用,在 ARC 下甥捺,會(huì)根據(jù)變量的其他修飾關(guān)鍵字進(jìn)行強(qiáng)引用或弱引用抢蚀。
3)block的屬性修飾詞為什么是 copy?使用block有哪些使用注意镰禾?
為什么是 copy:block 沒有被 copy 之前思币,是在內(nèi)存中的棧上或者全局區(qū),生命周期由系統(tǒng)管理羡微;而 copy 之后會(huì)被 copy 到堆上,此時(shí) block 生命周期由代碼管理惶我。
使用注意:循環(huán)引用問題妈倔,會(huì)導(dǎo)致內(nèi)存泄漏。
4)block 在修改 NSMutableArray (添加或移除元素)绸贡,需不需要添加 __block盯蝴?
不需要。因?yàn)閷?NSMutableArray 添加或移除元素不需要修改變量的值听怕。
Runtime
1捧挺、消息機(jī)制
1)講一下 OC 的消息機(jī)制
OC 對象調(diào)用方法底層轉(zhuǎn)為 objc_msgSend 函數(shù)調(diào)用,即給對象(消息接受者)發(fā)送一條消息(selector 方法名)尿瞭;
objc_msgSend 函數(shù)底層實(shí)現(xiàn)大致分為三大階段:消息發(fā)送闽烙,動(dòng)態(tài)方法解析,消息轉(zhuǎn)發(fā)声搁;
消息發(fā)送:通過對象的 isa 指針找到類對象或元類對象(取決于消息接受者是實(shí)例對象還是類對象黑竞,這里假設(shè)是實(shí)例對象,類對象的處理類似)疏旨,依次在類對象中的方法緩存列表和方法列表中查找消息的 selector 方法名很魂,若找到則直接調(diào)用方法,否則繼續(xù)從父類的方法列表中查找檐涝,一直找到基類為止遏匆;若查找到基類也沒有找到方法法挨,則進(jìn)入動(dòng)態(tài)方法解析階段;
動(dòng)態(tài)方法解析:實(shí)現(xiàn) -resolveInstanceMethod:(SEL)sel 方法幅聘,并調(diào)用 runtime 的方法 class_addMethod凡纳,為對象動(dòng)態(tài)地添加一個(gè)方法,然后再次進(jìn)入消息發(fā)送階段喊暖,并標(biāo)記已經(jīng)嘗試過動(dòng)態(tài)方法解析惫企;若添加了方法,則直接調(diào)用陵叽;若沒有添加狞尔,則進(jìn)入消息轉(zhuǎn)發(fā)階段;
消息轉(zhuǎn)發(fā):實(shí)現(xiàn) -forwardingTargetForSelector: 方法巩掺,返回一個(gè)新的對象做為消息接受者偏序,并調(diào)用 objc_msgSend 方法;若沒有返回新的對象而是返回 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ī)制流程
同問題1)
3)什么是Runtime燃箭?平時(shí)項(xiàng)目中有用過么冲呢?
Runtime:OC 是一門動(dòng)態(tài)性很強(qiáng)的語言,很多操作允許推遲到程序運(yùn)行時(shí)再執(zhí)行招狸;
OC 的動(dòng)態(tài)性是由 Runtime 來實(shí)現(xiàn)的敬拓,Runtime 是一套 C 語言的 API,封裝了很多動(dòng)態(tài)性相關(guān)的函數(shù)裙戏;
平時(shí)編寫的 OC 代碼乘凸,底層都是轉(zhuǎn)換為了 Runtime 的 API 進(jìn)行調(diào)用。
用途:給分類添加屬性實(shí)現(xiàn)(關(guān)聯(lián)對象 AssociateObject)累榜;
方法交換(method_exchange)营勤;
遍歷類的所有成員變量(歸檔解檔,獲取私有成員如textField._placeholderLabel壹罚,字典轉(zhuǎn)模型)
利用消息轉(zhuǎn)發(fā)機(jī)制冀偶,監(jiān)測方法找不到的問題;
...