Objective-C Basics
1.請說明并比較以下關鍵詞:strong, weak, assign, copy
- strong 表示指向并擁有該對象。其修飾的對象引用計數(shù)會增加1,該對象只要引用計數(shù)不為0就不會被銷毀,當然強行將其設置為nil,可以銷毀它。
- weak 表示指向但不擁有該對象。其修飾的對象引用計數(shù)不會增加,無需手動設置,該對象會自行在內(nèi)存中銷毀。
- assign 主要用于修飾基本數(shù)據(jù)類型。如NSInteger和CGFloat,這些數(shù)值主要存在棧上。
- weak 一般用來修飾對象,assign一般用來修飾基本數(shù)據(jù)類型。原因是assign修飾的對象被釋放后,指針的地址依然存在,造成野指正,在堆上容易造成奔潰,而棧上的內(nèi)存系統(tǒng)會自動處理,不會造成野指針。
- copy與strong類似。不同之處是strong的復制是多個指針指向同一個地址,而copy的復制每次會在內(nèi)存中拷貝一份對象,指針指向不同地址。copy一般用在修飾有可變對應類型的不可變對象上,如NSString,NSArray,NSDictionary。
- Objective-C 中,基本數(shù)據(jù)類型的默認關鍵字是atomic, readwrite, assign;普通屬性的默認關鍵字是atomic, readwrite, strong。
2.請說明并比較以下關鍵詞:__weak,__block
- __weak 與weak基本相同。前者用于修飾變量,后者用于修飾屬性,__weak 主要用于防止block中的循環(huán)引用。
- __block 也用于修飾變量,它是引用修飾,所以其修飾的值是動態(tài)變化的,即可以被重新賦值的。__block用于修飾某些block內(nèi)部將要修改的外部變量。
- __weak和__block 的使用場景幾乎與block息息相關。而所謂block,就是Objective-C對于閉包的實現(xiàn)。閉包就是沒有名字的函數(shù),或者理解為指向函數(shù)的指針。
3.請說明并比較以下關鍵詞:atomatic,nonatomic
- atomatic修飾的對象會保證setter和getter的完整性,任何線程對其訪問都可以得到完成的初始化后的對象。因為要保證操作完成,所以速度慢。它比nonatomic安全,但也并不是絕對的線程安全,例如多個線程同時調(diào)用set和get就會導致獲得的對象值不一樣。絕對的線程安全要用關鍵詞synchronized。
- nonatomic修飾的對象不保證setter和getter的完整性,所以多個線程對它進行訪問,它可能會返回為初始化的對象。正因為如此,它比atomic快,但也是線程不安全的。
4.什么是ARC
ARC全稱是Automatic Reference Counting,是Objective-C的內(nèi)存管理機制。簡單來說,就是代碼自動加了retain/release,原先需要手動添加的用來處理內(nèi)存管理的引用計數(shù)的代碼可以自動的由編譯器完成了。
ARC的使用是為了解決對象retain和release匹配的問題。以前手動管理造成內(nèi)存泄露或者重復釋放的問題將不復存在。
以前需要手動的通過retain去為對象獲取內(nèi)存,并用release釋放內(nèi)存。所以以前的操作稱為MRC(Manual Reference Counting)。
5.什么情況下會出現(xiàn)循環(huán)引用?
循環(huán)引用是指2個或以上對象互相強引用,導致所有對象無法釋放的現(xiàn)象。舉個例子:
class Father
@interface Father:NSobject
@property (strong,nonatomic) Son *son;
@end
class Son
@interface Son:NSobject
@property (strong,nonatomic) Father *father;
@end
上述代碼有兩個類,分別為Father和Son,F(xiàn)ather對Son強引用,Son對Father強引用。這樣釋放Son必須要先釋放Father,要釋放Father必須先釋放Son。如此一來,兩個對象都無法釋放。
解決方法是將Father中的Son對象屬性從strong改為weak。
6.以scheduledTimerWithTimeInterval的方式觸發(fā)的timer,在滑動頁面上的列表時,timer會暫停,為什么?該如何解決?
原因在于滑動時當前線程的runloop切換了model用于列表滑動,導致timer暫停。
runloop中的model主要用來指定事件在runloop中的優(yōu)先級,有以下幾種:
- Default(NSDefaultRunLoopMode):默認,一般情況下使用;
- Connection(NSConnectionReplyMode):一般系統(tǒng)用來處理NSConnection相關事件,開發(fā)者一般用不到;
- Modal(NSModalPanelRunLoopMode):處理modal panels事件;
- Event Tracking (NSEventTrackingRunLoopMode):用于處理拖拽和用戶交互的模式;
- Common (NSRunloopCommonModes):模式合集。默認包括Default、Modal、Event Tracking三大模式,可以處理幾乎所有事件。
回到題中的情境。滑動列表時,runloop的model由原來的Default模式切換到了Event Tracking模式,timer原來好好的運行在Default模式中,被關閉后自然就停止工作了。
解決方法其一是將timer加入到NSRunloopCommonModes中。其二是將timer放到另一個線程中然后開啟另一個線程的runloop,這樣可以保證與主線程互不干擾,而現(xiàn)在主線程正在處理頁面滑動。示例代碼如下:
//方法1
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//方法2
dispath_async(dispath_get_global_queue(0,0),^{
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(repeat:) userInfo:nil repeats:true];
[[NSRunLoop currentRunLoop] run];
});
7.說一下ios觸摸事件是怎么傳遞并響應的(響應者鏈 responder chain)
事件傳遞鏈
假設用戶觸摸了上圖的 View E 區(qū)域,那么 ios 將會按下面的順序
反復檢測 subview 來尋找 Hit-Test View
- 觸摸區(qū)域在視圖 A 內(nèi),所以檢測視圖 A 的 subview B 和 C ;
- 觸摸區(qū)域不在視圖 B 內(nèi) ,但是在視圖 C 內(nèi),所以檢查視圖 C 的subview D 和 E;
- 觸摸區(qū)域不在視圖 D 內(nèi),在視圖 E 中;
視圖 E 在整個視圖體系中是 lowest view,所以視圖 E 就是 Hit-Test View(能夠響應事件的那個最合適的view)。
總結:事件的傳遞鏈由系統(tǒng)向離用戶最近的view傳遞(父視圖向子視圖傳遞)。UIKit -> Application event queue -> window -> root view -> ... -> lowest view
事件響應鏈
- 響應者鏈通常是由 initial view 開始;
- UIView 的 nextResponder 是它的 superview ;如果 UIView 已經(jīng)是其所在的UIViewController 的top view,那么 UIView 的 nextResponder 就是 UIViewController;
- UIViewController 如果有 Super ViewController,那么它的 nextResponder 為其 Super ViewController 最表層的 view;如果沒有,那么它的 nextResponder 就是 UIWindow;
- UIWindow 的 contentView 指向 UIApplication,將其作為 nextResponder;
- UIApplication 是一個響應者鏈的終點,它的 nextResponder 指向 nil,整個響應鏈結束。
總結:響應鏈由離用戶最近的view向系統(tǒng)傳遞(事件傳遞鏈的反方向)。initial view -> super view -> ... view controller -> window -> Application