Nelson 寫些 iOS 開發的東東

如何解決 NSTimer 造成的 retain cycle

| Comments

故事是這麼開始的

最近在替公司 app 做健康檢查,找到一些 memory leaks 的問題,其中一個就是由 NSTimer 所引起的 retain cycle。

NSTimer 是個很容易造成 retain cycle 的物件,無論是新手或是老手都很可能一個不留意就踩到這個坑。舉個很常見的例子,這樣寫就產生 retain cycle 了:

@interface MyViewController()
@property (nonatomic, strong) NSTimer *timer;
@end
  
@implementation MyViewController
- (void)viewDidLoad {
  [super viewDidLoad];
  self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];
}
- (void)dealloc {
  [_timer invalidate];
  _timer = nil;
}
- (void)timerFired:(NSTimer *timer) {
  // Do something...
}
@end

一個 AFNetworking 的 retain cycle 問題

| Comments

AFNetworking 封裝了網路連線的許多工作,讓 iOS/Mac 開發者可以用簡潔的寫法去處理連線,但你知道要如何正確使用,才不會出現 retain cycle 嗎?

舉個例子

舉個最簡單的例子,我們可能會在自訂的 UIViewController 裡頭建立一個 AFHTTPSessionManager,透過它來進行網路連線,大部分的寫法大概如下:

@interface MyViewController ()
@property (nonatomic, strong) AFHTTPSessionManager *manager;
@end

@implementation MyViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.manager = [AFHTTPSessionManager manager];
    // Do something with self.manager...
}
@end

簡單直覺,對吧?但是你會發現在 view controller 被摧毀之後,這個 AFHTTPSessionManager 還存留在記憶體裡面,不信的話可以用 Xcode 的 Memory Graph 檢查看看。

你不信邪,所以你可能會試著在 dealloc 把它設為 nil,這樣總該沒問題了吧?

- (void)dealloc {
    _manager = nil;
}

可惜的是,這樣做沒有用,它依然存在記憶體裡。怎!麽!可!能!原因在一開始就提到了,因為它有 retain cycle。正確釋放的做法如下,你需要先呼叫 invalidateSessionCancelingTasks: 這個函式:

- (void)dealloc {
    [_manager invalidateSessionCancelingTasks:YES];
    _manager = nil;
}

為什麼會這樣

因為 AFHTTPSessionManager 擁有一個 NSURLSession *session property,而且把這個 session 的 delegate 設為 self,而 NSURLSessiondelegate 設為 retain。所以它們互相擁有彼此,造成了 retain cycle。

當我們呼叫 invalidateSessionCancelingTasks: 函式,它會去呼叫 NSURLSessioninvalidateAndCancelfinishTasksAndInvalidate。根據蘋果文件,呼叫這兩個函式之後,NSURLSession 才會斷開它與 delegate 的關聯。至此,才打破 retain cycle。

解法

有兩個解法,第一個就是如上所述,記得最後要呼叫 invalidateSessionCancelingTasks: 來結束任務。第二個就是把 AFHTTPSessionManager 寫成 singleton,這樣有 retain cycle 也無所謂了。

為何 Startup 不該用 Swift

| Comments

最近跟朋友聊天,聊到說我不建議 startup 使用 Swift 開發 app,趁著有空紀錄一下為何我會這麼說。

還是得先聲明一下,Swift 是一個很酷的語言,我沒有不喜歡它,只是站在公司的角度,我認為 startup 不應該使用 Swift 開發它們的主力產品,而是應該用 Objective-C。

主要是因為以下幾點理由:

Swift 還不穩定

Swift 是一個很新的語言,大家都還在摸索怎樣才是 best practice,但它同時也是一個快速成長與變化的語言,可能去年的 best practice 今年就不適用了。此外它也是一個尚未穩定的語言,剛推出的 Swift 3 還不能向下相容呢。

身為 iOS 開發者,每年都要為了升級 iOS 跟 Xcode 花一番心力。如果選擇用 Swift,那就得多花時間來處理 Swift 版本升級,甚至還要處理第三方套件因為升級而無法使用的問題。這是可以避免的,你只要一開始使用 Objective-C 開發即可。

對 startup 來說時間特別寶貴,我認為把時間花在升級 Swift 並不值得,更別說還得承擔升級之後 app 壞掉的風險。

將有經驗的工程師拒於門外

如果工程師只會 Swift 不會 Objecitve-C,代表他的 iOS 開發資歷並不久,如果使用 Objecitve-C 開發,至少可以確保找來的工程師經驗會多一點,對 startup 來說找到有經驗的人是很重要的事(雖然通常很難...)

也有一些有經驗的工程師因為種種原因還沒學習 Swift,如果哪天公司跟這樣的工程師搭上線,卻因為對方不會 Swift 所以無法提供更多有用的建議給你,這樣不是很可惜嗎?

Objective-C 沒有不好

如果你的公司裡頭有工程師提議要用 Swift 開發,理由是因為「Swift 很潮、想玩玩看」,那你們公司可能找了一位只想追求新技術的人。

使用新技術沒有不好,但要有充分的理由,你開公司找人來上班不是為了實驗新技術的。Objective-C 很成熟,無論是 best practice 或是資源都很完整,沒有什麼理由不用它。


以上都是站在 startup 的角度來說明為何不要用 Swift 開發主力產品,如果你們是一個大公司,或者工程團隊有強者坐鎮,或者你只是想寫個 side project 玩玩看,當然就無所謂。

讓 Xcode 8 再度支援 plugins

| Comments

眾所皆知的,Xcode 8 把之前的 plugins 都擋掉了,然後推出了 Source Editor Extension,但 Source Editor Extension 的能力還很有限,基本上只能對「文字」操作。

所以就有人提出了一個解法,把 Xcode 8 app unsign,這樣就能讓 plugins 再度作用了。具體作法如下:

  1. 下載 MakeXcodePluginsWork
  2. chmod 755 makeXcodePluginsWork 然後執行它
  3. 啟動 Xcode,原有的 plugins 都回來了

這樣會把 Xcode.app 的 signing 關掉,會變得比較不安全(其實就算跟之前一樣而已),所以請自己小心,後果自負。附帶一提,Xcode 8 開始內建一些功能,有些 plugins 可以退休了,例如:

  • 可以高亮度當前列,從【Preferences -> Fonts & Colors】設定「Current Line」顏色
  • 可以產生註解文件,熱鍵是【Command + Option + /】

用 Objective-C 實作 Redux 架構

| Comments

前言

有一定的程式設計經驗之後,會愈來愈感受到程式架構的重要性,在 iOS app 開發的世界裡,最常見的莫過於 MVC 架構,因為它夠簡單而且是蘋果推薦的架構。但當你的程式越來越龐大,流程越來越複雜的時候,就會發現 MVC 架構已經無法滿足需求了。這幾年最為人所知的就是 MVP / MVVM / VIPER / Coordinator 這幾個模式。

我認為這些模式的著眼點都在於「UI」:它們假設你有一套辦法去存取或修改資料,然後它們提出的方案是關於如何處理「界面顯示 / 使用者互動 / 資料存取」之間的關係。

當程式越長越大,要儲存的狀態越來越多,不同畫面之間需要同步的資料也越來越多,我們該如何管理資料的存取、確保其一致性與正確性呢?Facebook 之前提出了 Flux 架構,後來有人提出改良版的 Redux 架構,不管是 Flux 還是 Redux,其重點都是在於「資料的流動是單向的,資料只有一份,並且只有一個角色可以修改資料」。

Flux / Redux 一開始提出是給網站使用的架構,後來有人把它套用到 iOS 開發,不過我查到的資料都是使用 Swift 實作。無可否認使用 Swift 來實作這套架構的確比較方便,只是我很好奇用 Objective-C 的話會有多困難,以下就是我的一些開發過程。