面試題

1.簡述KVC和KVO,其中KVO實(shí)現(xiàn)原理?

KVC : 鍵值編碼(Key-Value Coding),它是一種通過key值訪問類屬性的機(jī)制,而不是通過setter/getter方法訪問。其中 KVC 原理:當(dāng)調(diào)用- (void)setValue:(id)value forUndefinedKey:(NSString *)key時,KVC底層的執(zhí)行機(jī)制如下:

首先搜索對應(yīng)屬性的setter方法

如果沒有找到屬性的setter方法,則會檢查+ (BOOL)accessInstanceVariablesDirectly方法是否返回了YES(該方法默認(rèn)返回YES),如果返回了YES, 則KVC機(jī)制會搜索類中是否存在該屬性的成員變量,也就是_屬性名,存在則對該成員變量賦值。搜索成員變量名的順序是 _key,_isKey,key,isKey。

另外我們也可以通過重寫+ (BOOL)accessInstanceVariablesDirectly方法返回NO,這個時候KVC機(jī)制就會調(diào)用 - (void)setValue:(id)value forUndefinedKey:(NSString *)key。

如果沒有找到成員變量,調(diào)用 - (void)setValue:(id)value forUndefinedKey:(NSString *)key。

2.Block實(shí)現(xiàn)原理;堆上和棧上的數(shù)據(jù)如何同步?

block本質(zhì)上也是一個oc對象,他內(nèi)部也有一個isa指針。block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象。結(jié)構(gòu)體,在棧上的情況, Block中的指針只是指向棧上的__block變量, 而當(dāng)Block/__block變量被copy到堆上以后, 堆上Block會持有堆上__block變量. 而堆上的Block再次被調(diào)用copy時, 只是Block的引用計(jì)數(shù)+1而已, 而__block變量如果被多個堆上Block持有也只涉及到引用記數(shù)的變化. 一旦Block/__block變量的引用計(jì)數(shù)為0, 就會自動從堆上釋放內(nèi)存.這里Block/__block變量在堆上的內(nèi)存管理與Objective-C對象完全一致.

3.推送如何實(shí)現(xiàn)的?

1.由App向iOS設(shè)備發(fā)送一個注冊通知,用戶需要同意系統(tǒng)發(fā)送推送。

2.iOS應(yīng)用向APNS遠(yuǎn)程推送服務(wù)器發(fā)送App的Bundle Id和設(shè)備的UDID。

3.APNS根據(jù)設(shè)備的UDID和App的Bundle Id生成deviceToken再發(fā)回給App。

4.App再將deviceToken發(fā)送給遠(yuǎn)程推送服務(wù)器(自己的服務(wù)器), 由服務(wù)器保存在數(shù)據(jù)庫中。

5.當(dāng)自己的服務(wù)器想發(fā)送推送時, 在遠(yuǎn)程推送服務(wù)器中輸入要發(fā)送的消息并選擇發(fā)給哪些用戶的deviceToken,由遠(yuǎn)程推送服務(wù)器發(fā)送給APNS。

6.APNS根據(jù)deviceToken發(fā)送給對應(yīng)的用戶。

4.簡述weak的實(shí)現(xiàn)原理;

weak 關(guān)鍵字的作用弱引用,所引用對象的計(jì)數(shù)器不會加一,并在引用對象被釋放的時候自動被設(shè)置為 nil;

weak是有Runtime維護(hù)的weak表;3.weak釋放為nil過程

weak被釋放為nil,需要對對象整個釋放過程了解,如下是對象釋放的整體流程:

1、調(diào)用objc_release

2、因?yàn)閷ο蟮囊糜?jì)數(shù)為0,所以執(zhí)行dealloc

3、在dealloc中,調(diào)用了_objc_rootDealloc函數(shù)

4、在_objc_rootDealloc中,調(diào)用了object_dispose函數(shù)

5、調(diào)用objc_destructInstance

6、最后調(diào)用objc_clear_deallocating。

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

其實(shí)Weak表是一個hash(哈希)表,然后里面的key是指向?qū)ο蟮牡刂罚琕alue是Weak指針的地址的數(shù)組

總結(jié)

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

5. TCP 和 UDP 的區(qū)別

TCP 為傳輸控制層協(xié)議。這種協(xié)議提供面向連接的、可靠的、點(diǎn)到點(diǎn)的通信;

UDP 為用戶數(shù)據(jù)報(bào)協(xié)議。這種協(xié)議提供非連接的、不可靠的、點(diǎn)到多的通信但是比 TCP 快。

6. TCP 的三次握手

第一次握手:客戶端發(fā)送 syn 包(syn=j)到服務(wù)器,并進(jìn)入 SYN_SEND 狀態(tài) ,等待服務(wù)器確認(rèn);

第二次握手:服務(wù)器收到 syn 包,必須確認(rèn)客戶的 SYN(ack=j+1),同時自己也發(fā)送一個 syn 包(syn=k),即 SYN+ACK 包,此時服務(wù)器進(jìn)入 SYN+RECV 狀態(tài);

第三次握手:客戶端收到服務(wù)器的 SYN+ACK 包,向服務(wù)器發(fā)送確認(rèn)包 ACK(ack=k+1),此時發(fā)送完畢,客戶端和服務(wù)端進(jìn)入 ESTABLISHED 狀態(tài),完成三次握手

7. 屬性readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在那種情況下用

(1) readwrite 是可讀可寫特性;需要生成getter方法和setter方法時

(2) readonly 是只讀特性 只會生成getter方法 不會生成setter方法 ;不希望屬性在類外改變

(3) assign 是賦值特性,setter方法將傳入?yún)?shù)賦值給實(shí)例變量;僅設(shè)置變量時;

(4) retain 表示持有特性,setter方法將傳入?yún)?shù)先保留,再賦值,傳入?yún)?shù)的retaincount會+1;

(5) copy 表示賦值特性,setter方法將傳入對象復(fù)制一份;需要完全一份新的變量時。

(6) nonatomic 非原子操作,決定編譯器生成的setter getter是否是原子操作,atomic表示多線程安全,一般使用nonatomic

8. @property 的本質(zhì)是什么,ivar、getter、setter 是如何生成并添加到這個類中的

在普通 OC 對象中,@property 就是編譯器自動幫我們生成一個成員變量(ivar)和它的 setter 和 getter 方法,它大概生成了五個東西:

1. objc_ivar_$類名$屬性名稱 該屬性的偏移量

2. setter 與 getter 方法對應(yīng)的實(shí)現(xiàn)函數(shù)

3. ivar_list 成員變量列表

4. method_list 方法列表

5. prop_list 屬性列表

也就是說我們每次增加一個屬性,系統(tǒng)就會在成員變量列表中添加一個成員變量的描述,在方法列表中添加 setter 與 getter 方法的描述,在屬性列表中增加一個屬性描述,然后計(jì)算該屬性在對象中的偏移量,然后生成 setter 與 getter 方法對應(yīng)的實(shí)現(xiàn),在 setter 方法中從偏移量開始復(fù)制,在 getter 中從偏移量開始取值,為了能夠讀取正確的自己數(shù),系統(tǒng)對對象偏移量的指針類型進(jìn)行了類型強(qiáng)轉(zhuǎn)。

9. @synthesize 和 @dynamic 分別有什么作用

@property 有兩個對應(yīng)的詞,一個是 @synthesize,一個是 @dynamic。如果 @synthesize 和 @dynamic 都沒寫,那么默認(rèn)的就是 @syntheszie var = _var;

@synthesize 的語義是如果你沒有手動實(shí)現(xiàn) setter 方法和 getter 方法,那么編譯器會自動為你加上這兩個方法。

@dynamic 告訴編譯器,屬性的 setter 與 getter 方法由用戶自己實(shí)現(xiàn),不自動生成。(當(dāng)然對于 readonly 的屬性只需提供 getter 即可)。假如一個屬性被聲明為 @dynamic var,然后你沒有提供 @setter 方法和 @getter 方法,編譯的時候沒問題,但是當(dāng)程序運(yùn)行到 instance.var =someVar,由于缺 setter 方法會導(dǎo)致程序崩潰;或者當(dāng)運(yùn)行到 someVar = var時,由于缺 getter 方法同樣會導(dǎo)致崩潰。編譯時沒問題,運(yùn)行時才執(zhí)行相應(yīng)的方法,這就是所謂的動態(tài)綁定。

10. get 與 post 的區(qū)別

get 是向服務(wù)器索取數(shù)據(jù)的一種請求,post 是向服務(wù)器提交數(shù)據(jù)的一種請求;

get 沒有請求體,post 有請求體;

get 使用 url 或 cookie 傳參,而 post 將數(shù)據(jù)放在 body 中;

get 請求的數(shù)據(jù)會暴露在地址欄中,而 post 不會,所以 post 比 get 更安全;

get 請求對 url 長度有限制,而 post 請求對 url 理論上沒有限制,但實(shí)際上,各個服務(wù)器會規(guī)定對 post 提交數(shù)據(jù)大小進(jìn)行限制。

11. 描述下 SDWebImage 里面給 UIImageView 加載圖片的邏輯

SDWebImage 中為 UIImageView 提供了一個分類 UIImageView+WebCache.h。這個分類中有一個最常用的方法:sd_setImageWithURL:placeholderImage: 會在真實(shí)圖片出來前先顯示占位圖片,當(dāng)真實(shí)圖片被加載出來后再替換占位圖片。

加載過程大致如下:

首先會在 SDWebImageCache 中尋找圖片是否有對應(yīng)的緩存,它會以 url 作為數(shù)據(jù)索引先在內(nèi)存中尋找是否有對應(yīng)的緩存,如果緩存未找到就會利用通過 MD5 處理過的 key 來繼續(xù)在磁盤中查詢對應(yīng)的數(shù)據(jù),如果找到了,就會把磁盤中的數(shù)據(jù)加載到內(nèi)存中,并將圖片顯示出來,如果在內(nèi)存中和磁盤緩存中都沒有找到,就會向遠(yuǎn)程服務(wù)器發(fā)送請求,開始下載圖片,下載后的圖片會加載到緩存中,并寫入磁盤中。整個獲取圖片的過程都是在子線程中執(zhí)行,獲取到圖片后回到主線程將圖片顯示出來。

原理:

從內(nèi)存中找到圖片,找到直接使用;

從沙盒中找,找到使用,緩存到內(nèi)存中;

從網(wǎng)絡(luò)上獲取,使用,緩存到內(nèi)存,緩存到沙盒。

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

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