iOS面試題總結(jié)(一)

1.為什么不能給類(lèi)別category 添加成員變量?extension呢?

2.isKindOfClass: 和 -isMemberOfClas區(qū)別?

3.weak的實(shí)現(xiàn)原理

4.理解 [self class] 與 [super class] ?

5.ios中的內(nèi)存管理機(jī)制

6.Block如何訪問(wèn)外部變量? 下劃線__block的作用? 如何防止循環(huán)引用?

7.Block循環(huán)引用問(wèn)題
(1).為什么Msonry不會(huì)循環(huán)引用?
(2).weakSelf、strongSelf結(jié)合使用

8.深拷貝與淺拷貝

9.KVO實(shí)現(xiàn)原理

10.Runtime的原理及實(shí)際使用場(chǎng)景

講解

1.為什么不能給類(lèi)別category 添加成員變量?extension呢?
分類(lèi)并不會(huì)改變?cè)蓄?lèi)的內(nèi)存分布的情況,它是在運(yùn)行期間決定的,此時(shí)內(nèi)存的分布已經(jīng)確定,若此時(shí)再添加實(shí)例會(huì)改變內(nèi)存的分布情況,這對(duì)編譯性語(yǔ)言是災(zāi)難,是不允許的。
category 是基于運(yùn)行時(shí)的:

typedef struct category_t {
const char *name; //類(lèi)的名字
classref_t cls; //類(lèi)
struct method_list_t *instanceMethods; //category中所有給類(lèi)添加的實(shí)例方法的列表
struct method_list_t *classMethods; //category中所有添加的類(lèi)方法的列表
struct protocol_list_t *protocols; //category實(shí)現(xiàn)的所有協(xié)議的列表
struct property_list_t *instanceProperties; //category中添加的所有屬性
} category_t;

結(jié)構(gòu)體中并沒(méi)有成員變量這個(gè)list,所以無(wú)法在category添加成員變量,添加屬性是可以的,但是不會(huì)生成添加屬性的getter和setter方法,所以,盡管添加了屬性,也無(wú)法使用點(diǎn)語(yǔ)法調(diào)用getter和setter方法,但你可以使用運(yùn)行時(shí)實(shí)現(xiàn)關(guān)聯(lián)對(duì)象并可以引用。

反觀擴(kuò)展(extension),作用是為一個(gè)已知的類(lèi)添加一些私有的信息,必須有這個(gè)類(lèi)的源碼,才能擴(kuò)展,它是在編譯器生效的,所以能直接為類(lèi)添加屬性或者實(shí)例變量。

category跟extension最大的區(qū)別在于生效時(shí)間不一樣,category在運(yùn)行時(shí)生效,而extension在編譯時(shí)生效

2.isKindOfClass: 和 -isMemberOfClas區(qū)別?
isKindOfClass來(lái)確定一個(gè)對(duì)象是否是一個(gè)類(lèi)的成員,或者是派生自該類(lèi)的成員。一個(gè)實(shí)例對(duì)象的類(lèi)對(duì)象或類(lèi)對(duì)象的元類(lèi)是否與當(dāng)前類(lèi)相等,或派生自當(dāng)前類(lèi))
isMemberOfClass只能確定一個(gè)對(duì)象是否是當(dāng)前類(lèi)的成員。(一個(gè)實(shí)例對(duì)象的類(lèi)對(duì)象或類(lèi)對(duì)象的元類(lèi)是否與當(dāng)前類(lèi)相等
詳解
類(lèi)方法+ (BOOL)isKindOfClass:(Class)cls
元類(lèi) --> 根元類(lèi) --> 根類(lèi) --> nil 與 傳入類(lèi)的對(duì)比
這里會(huì)先取類(lèi)對(duì)象的isa (self->ISA()),類(lèi)對(duì)象的isa指向的是元類(lèi),判斷元類(lèi)是否與cls相等,而且這里是一個(gè)for循環(huán),不相等的話會(huì)往元類(lèi)的父類(lèi)(tcls = tcls->superclass)循環(huán)去查找對(duì)比。
實(shí)例方法- (BOOL)isKindOfClass:(Class)cls,
對(duì)象的類(lèi) --> 父類(lèi) --> 根類(lèi) --> nil 與 傳入類(lèi)的對(duì)比
這里是先取實(shí)例對(duì)象的isa ([self class]),也就是類(lèi)對(duì)象,判斷類(lèi)對(duì)象與cls是否相等,不相等的話會(huì)往類(lèi)對(duì)象的父類(lèi)(tcls = tcls->superclass)循環(huán)去查找對(duì)比。

類(lèi)方法+ (BOOL)isMemberOfClass:(Class)cls
類(lèi)的元類(lèi) 與 傳入類(lèi) 對(duì)比
判斷的是類(lèi)對(duì)象的isa(即元類(lèi))是否等于cls。
實(shí)例方法- (BOOL)isMemberOfClass:(Class)cls,
對(duì)象的父類(lèi) 與 傳入類(lèi) 對(duì)比
判斷的是實(shí)例對(duì)象調(diào)用class方法的返回值(也就是類(lèi)對(duì)象)是否與cls相等。
iOS 捋清楚 isKindOfClass 與 isMemberOfClass
iOS 底層原理探索之 isKindOfClass & isMemberOfClass

3.weak的實(shí)現(xiàn)原理

weak是Runtime維護(hù)了一個(gè)hash(哈希)表,用于存儲(chǔ)指向某個(gè)對(duì)象的所有weak指針。weak表其實(shí)是一個(gè)hash(哈希)表,Key是所指對(duì)象的地址,Value是weak指針的地址(這個(gè)地址的值是所指對(duì)象指針的地址)數(shù)組。

對(duì)象準(zhǔn)備釋放時(shí),調(diào)用clearDeallocating函數(shù)。clearDeallocating函數(shù)首先根據(jù)對(duì)象地址獲取所有weak指針地址的數(shù)組,然后遍歷這個(gè)數(shù)組把其中的數(shù)據(jù)設(shè)為nil,最后把這個(gè)entry從weak表中刪除,最后清理對(duì)象的記錄。

        A.x = B;
 __weak B.y = A;

當(dāng)A釋放時(shí),會(huì)根據(jù)A的地址獲取所有弱引用它的指針的地址(如B.y),把它置為nil。
4.理解 [self class] 與 [super class] ?

我們知道實(shí)際上在iOS中,對(duì)方法的調(diào)用是通過(guò)發(fā)送消息來(lái)完成的。也就是說(shuō)使用 [self class] 時(shí),會(huì)使用obj_msgSend(id theReceiver, SEL selector, ...)函數(shù)向Receiver來(lái)發(fā)送消息。而使用 [super class] 時(shí),會(huì)使用obj_msgsendSuper(...)函數(shù)向Receiver來(lái)發(fā)送消息。
————————————————

@implementation Son : Father 
- (id)init 
{ 
self = [super init]; 
if (self) { 
NSLog(@”%@”, NSStringFromClass([self class])); 
NSLog(@”%@”, NSStringFromClass([super class])); 
} 
return self; 
} 
@end

上邊代碼會(huì)輸出什么,為什么
簡(jiǎn)單來(lái)說(shuō),self和super都是指向當(dāng)前實(shí)例的,不同的是,[self class]會(huì)在當(dāng)前類(lèi)的方法列表中去找class這個(gè)方法,[super class]會(huì)直接開(kāi)始在當(dāng)前類(lèi)的父類(lèi)中去找calss這個(gè)方法,兩者在找不到的時(shí)候,都會(huì)繼續(xù)向祖先類(lèi)查詢(xún)class方法,最終到NSObject類(lèi)。那么問(wèn)題來(lái)了,由于我們?cè)贔ather和Son中都沒(méi)有去重寫(xiě)class這個(gè)方法,最終自然都會(huì)去執(zhí)行NSObject中的class方法,結(jié)果也自然應(yīng)該是一樣的。
至于為什么是Son,我們可以看看NSObject中class的實(shí)現(xiàn):

-(Class)class { 
return object_getClass(self); 
}

5.ios中的內(nèi)存管理機(jī)制
為了管理所有對(duì)象的引用計(jì)數(shù)和weak指針,蘋(píng)果創(chuàng)建了一個(gè)全局的SideTables,雖然名字后面有個(gè)"s"不過(guò)他其實(shí)是一個(gè)全局的Hash表,里面的內(nèi)容裝的都是SideTable結(jié)構(gòu)體而已。它使用對(duì)象的內(nèi)存地址當(dāng)它的key。管理引用計(jì)數(shù)和weak指針就靠它了。

圖片.png

6.Block如何訪問(wèn)外部變量? 下劃線__block修飾auto變量的作用? 如何防止循環(huán)引用?
auto變量含義:離開(kāi)作用域(大括號(hào)),會(huì)自動(dòng)釋放的變量

  • block在訪問(wèn)auto變量(局部變量)時(shí),block內(nèi)部會(huì)捕獲到外部變量的值,后面修改外部auto變量的值,block內(nèi)部的值不會(huì)隨著改變而改變

  • block在訪問(wèn)static變量(局部變量)時(shí),block內(nèi)部會(huì)捕獲到外部變量的地址值,所以后面修改外部static變量值的時(shí)候,通過(guò)地址訪問(wèn)到的是最新修改后的值。

  • 當(dāng)auto變量加了__block修飾,編譯器會(huì)將__block變量包裝成一個(gè)對(duì)象,會(huì)捕獲到block內(nèi)部,并進(jìn)行指針傳遞,所以能夠修改其值(類(lèi)似于訪問(wèn)static變量)。

另外,靜態(tài)變量、全局變量和全局靜態(tài)變量,傳入的就是地址值,可以直接被block修改

  • 為了避免循環(huán)引用,最好的是對(duì)block內(nèi)部引用的self對(duì)象進(jìn)行弱引用。一般使用一個(gè)弱指針來(lái)指向該對(duì)象,然后在block內(nèi)使用該弱引用指針來(lái)進(jìn)行操作,這樣就避免了block對(duì)該對(duì)象的強(qiáng)引用。

7.Block循環(huán)引用問(wèn)題
(1).為什么Msonry不會(huì)循環(huán)引用?
查看masonry源碼可以看到究竟:msonry中設(shè)置布局的方法中的block對(duì)象并沒(méi)有被View所引用,而是直接在方法內(nèi)部同步執(zhí)行,執(zhí)行完以后block將釋放,其中捕捉的外部變量的引用計(jì)數(shù)也將還原到之前。
(2).weakSelf、strongSelf結(jié)合使用
使用weakSelf結(jié)合strongSelf的情況下,能夠避免循環(huán)引用,也不會(huì)造成提前釋放導(dǎo)致block內(nèi)部代碼無(wú)效(野指針問(wèn)題)。

_person1 = [[Person alloc] init];
_person2 = [[Person alloc] init];
_person2.name = @"張三";
__weak __typeof(self) weakSelf = self;
_person1.block = ^{
    __typeof(&*weakSelf) strongSelf = weakSelf;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"%@",strongSelf.person2.name);
    });
};

8.深拷貝與淺拷貝
淺拷貝就是拷貝之后,并沒(méi)有真正的復(fù)制,而是復(fù)制對(duì)象和原對(duì)象都指向同一個(gè)地址
深拷貝是真正的復(fù)制了一份,復(fù)制的對(duì)象只想新的地址

9.KVO實(shí)現(xiàn)原理
當(dāng)某個(gè)類(lèi)的對(duì)象屬性第一次被觀察時(shí),系統(tǒng)就會(huì)在運(yùn)行期動(dòng)態(tài)地創(chuàng)建該類(lèi)的一個(gè)派生類(lèi),在這個(gè)派生類(lèi)中重寫(xiě)基類(lèi)中任何被觀察屬性的setter方法。派生類(lèi)在被重寫(xiě)的setter方法內(nèi)實(shí)現(xiàn)真正的通知機(jī)制。
當(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ì)象屬性值的更改情況。
被觀察屬性發(fā)生改變之前,willChangeValueForkey:被調(diào)用,通知系統(tǒng)該keyPath的屬性值即將變更;當(dāng)改變發(fā)生后,didChangeValueForkey:被調(diào)用,通知系統(tǒng)該keyPath的屬性值已經(jīng)變更;之后,observeValueForKey:ofObject:context:也會(huì)被調(diào)用。且重寫(xiě)觀察屬性的setter方法這種繼承方式的注入是在運(yùn)行時(shí)而不是編譯時(shí)實(shí)現(xiàn)的。

10.Runtime的原理及實(shí)際使用場(chǎng)景
主動(dòng)使用
1.字典轉(zhuǎn)模型
2.給分類(lèi)屬性添加get,set方法
3.方法交換swizzling
4.設(shè)置UITextField占位文字的顏色
···

隱式調(diào)用
1.KVO與KVC的實(shí)現(xiàn)。
2.內(nèi)存管理,weak表的維護(hù)。
···

最后編輯于
?著作權(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ù)。

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