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 也無所謂了。