學習筆記,學習筆記封面
一、NSTimer使用時有哪些需要注意點?
1、必須保證有一個活躍的RunLoop。
NSTimer是基於RunLoop的一種定時機制,這涉及到預設主線程和子線程RunLoop的知識延伸;同時還有頁面滑動時防止定時器失效的知識點:
解決方案:[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
2、NSTimer的建立與撤銷必須在同一個線程操作,不能跨越線程操作。
3、存在記憶體流失的風險
使用NSTimer時,timer會保持對target和userInfo參數的強引用。只有當調取了NSTimer的invalidate方法時,NSTimer才會釋放target和userInfo。產生timer的方法中如果repeats參數為NO,則定時器觸發後會自動調取invalidate方法。如果repeats參數為YES,則需要手動調取invalidate方法才能釋放timer對target和userIfo的強引用。
很搞笑,以前自己還寫過一篇部落格叫《實現定時器NSTimer的高逼格方法->GCD》講的就是使用NSTimer需要注意的點以及使用GCD如何規避。當天面試時,可能是前面關於runtime的問題沒有回答好,造成情緒波動,影響了這題的發揮。當然我也必須正視自己對知識點還沒有爛熟於心,需要多溫習鞏固。
我的部落格關於這篇文章傳送門:http://www.cnblogs.com/beckwang0912/p/7027484.html
二、__weak如何?對象值自動化佈建為nil的?
這個問題在《Objective-C 進階編程》裡面1.4的章節有_weak修飾符的詳細介紹。書本也很久前看過,當天也就記起來一個內部實現是雜湊表。
在這裡參考書本簡單的說一下實現:
{ id __weak obj_weak = obj;}
假設變數obj附加__strong修飾符且對象被賦值。
編譯器類比代碼:
id obj_weak;obj_weak = 0;objc_storeWeak(&obj_weak,obj);objc_storeWeak(&obj,0);
從上面可以很明顯看出您提問的key和value分別是什麼,key是賦值對象obj的記憶體位址,value是obj_weak的記憶體位址。 這也可以解釋您當時追問的,多個weak指標指向同一個賦值地址,怎麼解決全部置nil的問題,表結構類似於下面:

置nil過程如下:
1. 從weak表中擷取廢棄對象的地址為索引值的記錄
2. 將包含在記錄中的所有附有__weak修飾符變數的地址,賦值為nil
3. 從weak表中刪除記錄
4. 從引用計數表中刪除廢棄對象的地址作為索引值的記錄
當時還有一個追問,說為什麼要置nil呢?我的回答是防止野指標。現在我按照__weak雜湊表的結構推測,防止野指標應該是可行的,因為若使用__weak修飾符的變數引用對象被廢棄時,不將該變數置nil,會造成該指標指向了一個不存在的地址,再次賦值或使用時可能造成crash。
三、Notification使用時如果在addObserver後不成對的使用removeObserver會怎麼樣?
這個問題,我當時回答的是沒有遇到過,確實我在工作中一直有很注意的這個問題,回來也特意檢查了一下工程代碼,還真有其他同事忘記移除了,但是程式也沒用崩潰過。這讓我很好奇,難道在因為蘋果的ARC在頁面pop出棧的時候自動把註冊的通知移除了?不然肯定會發生向野指標對象發送訊息,程式crash的情況。所有我特意寫了demo嘗試下,主要是思路:
1、首先在UIViewController中註冊通知,不移除。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];
移除注釋掉:
- (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; //[[NSNotificationCenter defaultCenter] removeObserver:self name:@"test" object:nil];}
2、navigationController進入到這個頁面,然後pop出去。最後點擊發送通知的按鈕事件。
TestViewController *vc = [[TestViewController alloc] init];[self.navigationController pushViewController:vc animated:YES];
pop出來後點擊按鈕:
- (void)clickBtn:(id)sender{ [[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil];}
我發現點擊按鈕後什麼都沒有發生,我事先準備好的日誌()沒有列印出來,程式也沒有崩潰。
- (void)test{ NSLog(@"current thread = %@", [NSThread currentThread]); NSLog(@"通知調用");}
這更加驗證了我的猜想,UIViewController自己銷毀的時候悄悄的幫我暗地裡移除了。有了這個想法之後,我來進一步的驗證,我的驗證思路是,我只要證明pop出來的時候調用了NSNotificationCenter的removeObserver方法就可以了。
#import "NSNotificationCenter+RemoveTest.h"@implementation NSNotificationCenter (RemoveTest)- (void)removeObserver:(id)observer{ NSLog(@"====%@ 測試是否移除通知===", [observer class]);}@end
按照上面的步驟再走一遍,神奇的事情發生了。
2017-07-27 10:33:31.610 beck.wang[1366:116266] ====__NSObserver 測試是否移除通知===2017-07-27 10:33:31.675 beck.wang[1366:116266] ====ViewController 測試是否移除通知===2017-07-27 10:34:52.939 beck.wang[1366:116266] ====TestViewController 測試是否移除通知===
說明我上面的推論是正確的,UIViewController自己銷毀的時候悄悄的幫我暗地裡移除了。
但是我還是推薦註冊和移除成對出現,防止一些意外情況的發生,蘋果也是這樣推薦的。我想蘋果之所以推薦註冊和移除成對出現,可能是為了方式同一個通知的重複註冊,不利於效能和記憶體,再者試想一下這樣的情境,如果註冊的通知沒有移除,而post的時候在接受訊息的對象已經釋放的情況下,是否形成了給不存在對象發送訊息的情況,程式是否會發生不可知的crash呢?這有待我去考證。
四、UITableViewCell的重用機制
原理: UITableView只會建立一螢幕(或一螢幕多一點)的UITableViewCell,其他都是從中取出來重用的。每當Cell滑出螢幕時,就會放入到一個集合(或數組)中(這裡就相當於一個重用池),當要顯示某一位置的Cell時,會先去集合(或數組)中取,如果有,就直接拿來顯示;如果沒有,才會建立。這樣做的好處可想而知,極大的減少了記憶體的開銷。
當時追問是這個“重用池”是怎麼設計的?經過提示後,我回答的是使用字典儲存,key是傳入的標誌符,value就是UITableViewCell。過後我查閱了資料,也只找到這樣的資料:UITableView標頭檔中,有NSMutableArray* visiableCells,和NSMutableDictnery* reusableTableCells兩個結構。visiableCells內儲存當前顯示的cells,reusableTableCells儲存可重用的cells。說明這個所謂的“重用池”應該就是可變字典集合[NSMutableDictnery* reusableTableCells],key為傳入的標誌符,value我猜測可能是個數組,用於儲存UITableViewCell。
五、從OC調用函數叫做發送訊息追問蘋果為什麼設計Runtime?設計它有什麼好處?
這個問題我回答的不好,回來後查閱資料,我在這裡說一點自己的愚見。
1、runtime最大的特性就是訊息轉寄機制
訊息派發的設計使得編譯期間Objective-C非常包容對象所屬的類。C++裡面我們可以基於稱之為模板的方式實現對“類”的自訂,OC通過統一基類比如NSObject(不僅僅只有NSObject,還可以是各類根協議)對所有類新增定義。你可以向任何包括null 指標nil在內的對象發你想發的訊息。訊息派發的機制使得在不重新編譯的情況下,在運行期間,幹預或者說hook原來的target(方法、變數等)變得更易於實現,更有實際應用價值。
2、runtime是OC記憶體管理模型的實現者
Objective-C的記憶體管理原理,簡單說就是“引用計數”機制。如果有模組需要引用一個對象,引用時會讓對象統計用的引用計數值加1,並記錄在對象的結構資訊當中,當模組不再需要該對象的時候則減1,而當該對象的引用計數值為0時,就可以認為該對象不再被需要,及時銷毀釋放記憶體(回收資源)。
3、Runtime允許我們通過標準的介面(C函數)對所有Objective-C類的變數、方法、屬性以及協議等等作查詢和動態擴充,從而達到我們豐富項目中語言和類庫特性的目標。
最後借鑒網上一張圖說明runtime的用法:
參考文章:
Runtime的十種用法
Runtime為什麼是這樣?