1、什么是arc?(arc是為了解決什么問題誕生的?)
首先解釋ARC: automatic reference counting自動引用計數(shù)。
ARC幾個要點:
在對象被創(chuàng)建時 retain count +1,在對象被release時 retain count -1.當(dāng)retain count 為0 時,銷毀對象。
程序中加入autoreleasepool的對象會由系統(tǒng)自動加上autorelease方法,如果該對象引用計數(shù)為0,則銷毀。
那么ARC是為了解決什么問題誕生的呢?這個得追溯到MRC手動內(nèi)存管理時代說起。
MRC下內(nèi)存管理的缺點:
1.當(dāng)我們要釋放一個堆內(nèi)存時,首先要確定指向這個堆空間的指針都被release了。(避免提前釋放)
2.釋放指針指向的堆空間,首先要確定哪些指針指向同一個堆,這些指針只能釋放一次。(MRC下即誰創(chuàng)建,誰釋放,避免重復(fù)釋放)
3.模塊化操作時,對象可能被多個模塊創(chuàng)建和使用,不能確定最后由誰去釋放。
4.多線程操作時,不確定哪個線程最后使用完畢
2、請解釋以下keywords的區(qū)別: assign vs weak, __block vs __weak
assign適用于基本數(shù)據(jù)類型,weak是適用于NSObject對象,并且是一個弱引用。
assign其實也可以用來修飾對象,那么我們?yōu)槭裁床挥盟兀恳驗楸籥ssign修飾的對象在釋放之后,指針的地址還是存在的,也就是說指針并沒有被置為nil。如果在后續(xù)的內(nèi)存分配中,剛好分到了這塊地址,程序就會崩潰掉。
而weak修飾的對象在釋放之后,指針地址會被置為nil。所以現(xiàn)在一般弱引用就是用weak。
首先__block是用來修飾一個變量,這個變量就可以在block中被修改(參考block實現(xiàn)原理)
__block:使用__block修飾的變量在block代碼快中會被retain(ARC下,MRC下不會retain)
__weak:使用__weak修飾的變量不會在block代碼塊中被retain
同時,在ARC下,要避免block出現(xiàn)循環(huán)引用 __weak typedof(self)weakSelf = self;
3、__block在arc和非arc下含義一樣嗎?
是不一樣的!!!
在MRC中__block variable在block中使用是不會retain的
但是ARC中__block則是會retain的。
取而代之的是用__weak或是__unsafe_unretained來更精確的描述weak reference的目的
其中前者只能在iOS5之后可以使用,但是比較好 (該物件release之后,此pointer會自動置成nil)
而后者是ARC的環(huán)境下為了相容4.x的解決方案。
所以上面的范例中
__block MyClass* temp = …; // MRC環(huán)境下使用
__weak MyClass* temp = …; // ARC但只支援iOS5.0以上的版本
__unsafe_retained MyClass* temp = …; //ARC且可以相容4.x以后的版本
4、使用nonatomic一定是線程安全的嗎?
不是的。
- atomic原子操作,系統(tǒng)會為setter方法加鎖。 具體使用 @synchronized(self){//code }
- nonatomic不會為setter方法加鎖。
- atomic:線程安全,需要消耗大量系統(tǒng)資源來為屬性加鎖
- nonatomic:非線程安全,適合內(nèi)存較小的移動設(shè)備
5、+(void)load; +(void)initialize;有什么用處?
在Objective-C中,runtime會自動調(diào)用每個類的兩個方法。+load會在類初始加載時調(diào)用,+initialize會在第一次調(diào)用類的類方法或?qū)嵗椒ㄖ氨徽{(diào)用。這兩個方法是可選的,且只有在實現(xiàn)了它們時才會被調(diào)用。
共同點:兩個方法都只會被調(diào)用一次。
6、為什么其他語言里叫函數(shù)調(diào)用, objective c里則是給對象發(fā)消息(或者談下對runtime的理解)
先來看看怎么理解發(fā)送消息的含義:
曾經(jīng)覺得Objc特別方便上手,面對著 Cocoa 中大量 API,只知道簡單的查文檔和調(diào)用。還記得初學(xué) Objective-c 時把[receiver message]當(dāng)成簡單的方法調(diào)用,而無視了“發(fā)送消息”這句話的深刻含義。于是[receiver message]會被編譯器轉(zhuǎn)化為:
objc_msgSend(receiver, selector)
如果消息含有參數(shù),則為:
objc_msgSend(receiver, selector, arg1, arg2, ...)
如果消息的接收者能夠找到對應(yīng)的selector,那么就相當(dāng)于直接執(zhí)行了接收者這個對象的特定方法;否則,消息要么被轉(zhuǎn)發(fā),或是臨時向接收者動態(tài)添加這個selector對應(yīng)的實現(xiàn)內(nèi)容,要么就干脆玩完崩潰掉。
現(xiàn)在可以看出[receiver message]真的不是一個簡簡單單的方法調(diào)用。因為這只是在編譯階段確定了要向接收者發(fā)送message這條消息,而receive將要如何響應(yīng)這條消息,那就要看運行時發(fā)生的情況來決定了。
Objective-C 的 Runtime 鑄就了它動態(tài)語言的特性,這些深層次的知識雖然平時寫代碼用的少一些,但是卻是每個 Objc 程序員需要了解的。
Objc Runtime使得C具有了面向?qū)ο竽芰?,在程序運行時創(chuàng)建,檢查,修改類、對象和它們的方法??梢允褂胷untime的一系列方法實現(xiàn)。
順便附上OC中一個類的數(shù)據(jù)結(jié)構(gòu) /usr/include/objc/runtime.h
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
//isa指針指向Meta Class,因為Objc的類的本身也是一個Object,
//為了處理這個關(guān)系,runtime就創(chuàng)造了Meta Class,
//當(dāng)給類發(fā)送[NSObject alloc]這樣消息時,
//實際上是把這個消息發(fā)給了Class Object
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父類
const char *name OBJC2_UNAVAILABLE; // 類名
long version OBJC2_UNAVAILABLE; // 類的版本信息,默認為0
long info OBJC2_UNAVAILABLE; // 類信息,供運行期使用的一些位標(biāo)識
long instance_size OBJC2_UNAVAILABLE; // 該類的實例變量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類的成員變量鏈表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法緩存,對象接到一個消息會根據(jù)isa指針查找消息對象,這時會在method Lists中遍歷,如果cache了,常用的方法調(diào)用時就能夠提高調(diào)用的效率。
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 協(xié)議鏈表
#endif
} OBJC2_UNAVAILABLE;
//OC中一個類的對象實例的數(shù)據(jù)結(jié)構(gòu)(/usr/include/objc/objc.h)
OC中一個類的對象實例的數(shù)據(jù)結(jié)構(gòu)(/usr/include/objc/objc.h):
typedef struct objc_class *Class;
// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
// A pointer to an instance of a class.
typedef struct objc_object *id;
向object發(fā)送消息時,Runtime庫會根據(jù)object的isa指針找到這個實例object所屬于的類,然后在類的方法列表以及父類方法列表尋找對應(yīng)的方法運行。id是一個objc_object結(jié)構(gòu)類型的指針,這個類型的對象能夠轉(zhuǎn)換成任何一種對象。
然后再來看看消息發(fā)送的函數(shù):objc_msgSend函數(shù)
在引言中已經(jīng)對objc_msgSend進行了一點介紹,看起來像是objc_msgSend返回了數(shù)據(jù),其實objc_msgSend從不返回數(shù)據(jù)而是你的方法被調(diào)用后返回了數(shù)據(jù)。下面詳細敘述下消息發(fā)送步驟:
檢測這個 selector 是不是要忽略的。比如 Mac OS X 開發(fā),有了垃圾回收就不理會 retain,release 這些函數(shù)了。
檢測這個 target 是不是 nil 對象。ObjC 的特性是允許對一個 nil 對象執(zhí)行任何一個方法不會 Crash,因為會被忽略掉。
如果上面兩個都過了,那就開始查找這個類的 IMP,先從 cache 里面找,完了找得到就跳到對應(yīng)的函數(shù)去執(zhí)行。
如果 cache 找不到就找一下方法分發(fā)表。
如果分發(fā)表找不到就到超類的分發(fā)表去找,一直找,直到找到NSObject類為止。
7、什么是method swizzling?
Method Swizzling 原理(方法攪拌?)
在Objective-C中調(diào)用一個方法,其實是向一個對象發(fā)送消息,查找消息的唯一依據(jù)是selector的名字。利用Objective-C的動態(tài)特性,可以實現(xiàn)在運行時偷換selector對應(yīng)的方法實現(xiàn),達到給方法掛鉤的目的。
每個類都有一個方法列表,存放著selector的名字和方法實現(xiàn)的映射關(guān)系。IMP有點類似函數(shù)指針,指向具體的Method實現(xiàn)。
方法指向
我們可以利用 method_exchangeImplementations 來交換2個方法中的IMP,
我們可以利用 class_replaceMethod 來修改類,
我們可以利用 method_setImplementation 來直接設(shè)置某個方法的IMP,
歸根結(jié)底,都是偷換了selector的IMP
8、instrument可以干什么?
Blank(空模板):創(chuàng)建一個空的模板,可以從Library庫中添加其他模板;
Activity Monitor(活動監(jiān)視器):顯示器處理的CPU、內(nèi)存和網(wǎng)絡(luò)使用情況統(tǒng)計;
Allocations(內(nèi)存分配):跟蹤過程的匿名虛擬內(nèi)存和堆的對象提供類名和可選保留/釋放歷史;
Automation(自動化):這個模板執(zhí)行它模擬用戶界面交互為IOS機應(yīng)用從instrument啟動的腳本;
Leaks(泄漏):一般的措施內(nèi)存使用情況,檢查泄漏的內(nèi)存,并提供了所有活動的分配和泄漏模塊的類對象分配統(tǒng)計信息以及內(nèi)存地址歷史記錄;
Time Profiler(時間探查):執(zhí)行對系統(tǒng)的CPU上運行的進程低負載時間為基礎(chǔ)采樣。
推薦??:
結(jié)實人脈、討論技術(shù) 你想要的這里都有!
搶先入群,跑贏同齡人!(入群無需任何費用)
(直接搜索群號:761407670,快速入群)
-
點擊加入:iOS開發(fā)交流群
整理的2020年《大廠最新常問iOS面試題+答案》,有需要的伙伴,直接加iOS技術(shù)交流群:761407670,免費獲??;群內(nèi)更有內(nèi)推機會!
申請即送:
BAT大廠面試題、獨家面試工具包,
資料免費領(lǐng)取,包括 數(shù)據(jù)結(jié)構(gòu)、底層進階、圖形視覺、音視頻、架構(gòu)設(shè)計、逆向安防、RxSwift、flutter,
作者:有度YouDo
原文鏈接:http://m.itdecent.cn/p/d2d8cf4e6ff6