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
,而 NSURLSession
把 delegate
設為 retain
。所以它們互相擁有彼此,造成了 retain cycle。
當我們呼叫 invalidateSessionCancelingTasks:
函式,它會去呼叫 NSURLSession
的 invalidateAndCancel
或 finishTasksAndInvalidate
。根據蘋果文件,呼叫這兩個函式之後,NSURLSession
才會斷開它與 delegate 的關聯。至此,才打破 retain cycle。
解法
有兩個解法,第一個就是如上所述,記得最後要呼叫 invalidateSessionCancelingTasks:
來結束任務。第二個就是把 AFHTTPSessionManager
寫成 singleton,這樣有 retain cycle 也無所謂了。