25._objc_msgForward()是做什麼的?直接呼叫它會發生什麼?
_objc_msgForward
()是IMP型別,是做訊息轉發的。當向一個物件傳送訊息,這個物件並沒有實現的時候,_objc_msgForward
會嘗試做訊息轉發。- 一旦呼叫
_objc_msgForward
的方法,回跳過查詢IMP的過程,直接出發訊息轉發 - 如果呼叫了這個方法,即使這個物件實現了這個方法,也會告訴
objc_msgSend
,並沒有找到這個方法
什麼時候會使用_objc_msgForward方法
- 你想獲取某方法所對應的NSInvocation物件
- JSPatch 就是直接呼叫
_objc_msgForward
來實現其核心功能的 - 同時 RAC(ReactiveCocoa) 原始碼中也用到了該方法
26.runtime如何實現weak的自動置空
runtime對註冊的類,會進行佈局,對於weak物件會放入一個hash表中,以weak指向的物件記憶體地址為key,weak物件為value,當weak所指向的物件retainCount為0時會dealloc,加入weak所指向的物件記憶體地址為a,那麼在hash表中找到所有以a為key的物件,從而設定為nil。
27.能否向編譯後得到的類中增加例項變數,能否向執行時動態新增的類中增加成員變數,為什麼?
- 不能向編譯後得到的類中增加例項變數
- 能向執行時動態新增的類中增加例項變數
解釋原因:
- 因為編譯後的類註冊在Runtime中,類的例項變數連結串列和例項變數的記憶體大小都是確定的。
- 執行時建立的類可以增加例項變數,呼叫
class_addIvar
函式。但是必須得在objc_allocateClassPair
和objc_registerClassPair
之間,原因如上。
28.Runloop和執行緒有什麼關係?
- Runloop和執行緒是一一對應的,Runloop是執行緒的基礎架構部分,有一個全域性字典儲存了Runloop,以執行緒為key,對應的Runloop為value。
- 主執行緒的Runloop是預設啟動的。
- 子執行緒的Runloop預設不啟動,當你獲取的時候會自動建立,並儲存字典,當執行緒被銷燬的時候,Runloop也會被銷燬。
- CFRunLoopGetCurrent()來獲取當前執行緒所在的Runloop,CFRunLoopGetMain()來獲取主執行緒的Runloop。
29.Runloop的Mode作用是什麼?
model主要是用來指定事件在執行迴圈中的優先順序,分為
- NSDefaultRunLoopMode,預設,空閒狀態
- UITrackingRunLoopMode,scrollView滑動是
- UIInitializationRunLoopMode,應用啟動時
- NSRunLoopCommonModes,前兩個的集合
蘋果公開提供的mode有兩個:NSDefaultRunLoopMode和NSRunLoopCommonModes
30.以+scheduledTimerWithTimeInterval的方式觸發的Timer,在滑動頁面上的列表時,Timer會暫停回撥,為什麼?
Runloop只能執行在一種mode下,+scheduledTimerWithTimeInterval方法建立的Timer預設會被新增到NSDefaultRunLoopMode中,當UIScrollView滑動時,當前mode被切換成UITrackingRunLoopMode,此時Runloop會停下重啟,無法再執行Timer。可以將Timer新增到NSRunLoopCommonModes中解決這個問題。
31.猜想Runloop內部是如何實現的
function loop(){
do{
var message = get_next_message;
process_message(message);
}while(message != quit);
}
複製程式碼
32.Objc使用什麼機制來進行記憶體管理
ARC(自動引用計數),通過retainCount來決定物件是否該被釋放,每次Runloop的時候,都會檢查物件的retainCount,當物件的引用計數為0是,呼叫dealloc釋放該物件。
33.ARC通過什麼方式幫助開發者管理記憶體
- 在編譯器,ARC用更底層的C介面實現retain/release/autorelease,這樣做效能更好,這也是為什麼在ARC下不能手動管理記憶體,同是,編譯器會忽略一些不必要的操作。
- 在執行期,做的優化比較複雜,暫時未知
34.不手動指定autoreleasePool的情況下,一個autoRelease物件在什麼時候釋放?(比如在一個VC的viewDidload中建立)
- 所有autorelease的物件,在出了作用域後,會被新增到最近建立的自動釋放池中,並會在當前的Runloop迭代結束時被釋放
- 從程式啟動到載入完成是一個完整的執行迴圈,然後會停下來,等待使用者互動,使用者的每一次互動都會啟動一次執行迴圈,來處理使用者所有的點選事件和觸控事件
- viewDidload和viewwillappear屬於同一迴圈,viewdidappear時已經被釋放了
35.BAD_ACCESS在什麼情況下會出現?
- 訪問了懸浮指標
- 訪問一個已經釋放了的物件的成員變數,或者發訊息
- 死迴圈
36.蘋果是如何實現autoReleasePool的?
autorelease是以一個佇列陣列的形式實現,主要通過下列三個函式完成。
objc_autoreleasepoolPush
objc_autoreleasepoolPop
objc_autorelease
37,使用block什麼時候會發生迴圈引用,如何結局?
一個物件強引用了block,block又強引用了該物件,就會出現迴圈引用。結局方法是用__block
或者__weak
修飾後的物件放到block中使用。
38.在block內如何修改外部變數
預設情況下,在block中訪問的外部變數是複製過去的,即:寫操作不對原變數生效。但是你可以加上 __block
來讓其寫操作生效。
- block中無法修改外部變數的原因是此時外部變數指的是棧中指標的記憶體地址
- 使用__block所起到的作用就是標記改物件,把該物件的記憶體地址copy到堆中,然後再對其進行修改
- 棧是紅燈區無法修改,堆是綠燈區
39.使用系統的某些blockAPI時,是否需要考慮迴圈引用
一般不需要考慮迴圈引用的問題
40.GCD的佇列(dispatch_queue_t)分哪兩種型別
- 序列佇列Serial Dispatch Queue
- 並行佇列Concurrent Dispatch Queue
41. 如何用GCD同步若干個非同步呼叫?
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{ /*載入圖片1 */ });
dispatch_group_async(group, queue, ^{ /*載入圖片2 */ });
dispatch_group_async(group, queue, ^{ /*載入圖片3 */ });
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 合併圖片
});複製程式碼
42.dispatch_barrier_async的作用是什麼?
在並行佇列中,為了保持某些任務的需要,需要等到一些任務完成後才能繼續進行下去,使用dispatch_barrier_async
來等待之前任務完成,避免資料競爭等問題。它會等待barrier之前的所有操作執行完畢後執行,並且在barrier執行完畢以後,barrier之後的操作才會得到執行。barrier必須要和dispatch_queue_create函式生成的並行佇列一起使用,否則就和dispatch_async
的作用一模一樣。
43.蘋果為什麼要廢棄dispatch_get_current_queue()函式
dispatch_get_current_queue容易造成死鎖
44.以下程式碼執行結果如何
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
}複製程式碼
發生主執行緒鎖死
45.addObserver: forKeyPath: options: context:各個引數的作用分別是什麼?observer中需要實現哪個方法才能達到回撥
/*
1.觀察者,負責處理監聽事件的物件
2.觀察的屬性
3.觀察的選項
4.上下文,用來區分不同的物件
*/
[person addObserver:self forKeyPath:@"lastName" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"Person Name"];
複製程式碼
observer需要實現下面這個方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
}
複製程式碼
46.如何手動出發一個value的KVO
-(void)setName:(NSString *)name
{
//儲存舊值
[self willChangeValueForKey:@"name"];
_name = name;
//儲存新值
[self didChangeValueForKey:@"name"];
}
//禁用系統的自動觸發
+(BOOL)automaticallyNotifiesObserversOfName
{
return NO;
}
複製程式碼
47.若一個類有例項變數NSString *_foo,呼叫setValue:forkey,可以以foo還是_foo為key
都可以,KVC同時支援例項變數和屬性
48.KVC的keyPath中的集合運算子如何使用
- 普通集合運算子有@sum,@count,@max,@min,@avg
- 格式為@"@sum.age"或者@"集合屬性.@sum.age"
- 必須用在集合物件上或者普通物件的集合屬性上
49.KVO和KVC的keyPath一定是屬性嗎
KVC支援例項變數,KVO可以手動設定支援,預設不支援
50.如何關閉預設的KVO的實現,並進入自定義的KVO實現
答案見第46
51.Apple用什麼方式實現對一個物件的KVO
- 當你註冊一個類!A的觀察者的時候,這個類會在執行時動態建立一個新類,這個類是A的派生類,命名為NSKVONotifying_A,並進行了isa混寫,使被觀察物件的isa指標指向這個新類,並重寫了被觀察屬性的set方法如下:
-(void)setName:(NSString *)name
{
[self willChangeValueForKey:@"name"];
[super setValue:name forKey:@"name"];
[self didChangeValueForKey:@"name"];
}複製程式碼
這種繼承和方法注入是在執行時而不是編譯時實現的,這就是正確命名如此重要的原因。
- 這幾個方法呼叫的過程如下:
addObserver: forKeyPath: options: context:
willChangeValueForKey
didChangeValueForKey
- 在
didChangeValueForKey
中回撥observeValueForKeyPath
- 我們只有在希望能控制回撥的呼叫時機時,才會手動出發KVO,回撥的時機就是你呼叫
didChangeValueForKey
方法時。
52.IBOutlet連出來的檢視屬性為什麼被設定為weak
使用storyboard(xib不行)建立的vc,會有一個叫_topLevelObjectsToKeepAliveFromStoryboard
的私有陣列強引用所有top level的物件,所以這時即便outlet宣告成weak也沒關係
53.IB中User Defined Runtime Attributes如何使用?
它能夠通過KVC的方式配置一些你在interface builder 中不能配置的屬性
54.如何除錯BAD_ACCESS錯誤
- 重寫object的
respondsToSelector
方法,顯示出現EXEC_BAD_ACCESS前訪問的最後一個object - Enable Address Sanitizer
- Enable Zombie Objects
- 設定全域性斷點快速定位問題程式碼所在位置
55.lldb(gdb)常用的除錯命令?
- breakpoint 設定斷點定位到某一個函式
- n 斷點指標下一步
- po列印物件
Posted by 微博@iOS程式犭袁,我有自己簡單整理了一下。