RAC中監聽通知的坑!

Se7en丶秦發表於2017-12-21

在很多文章裡面都介紹了RAC的用法,其中對於NSNotificationCenter的介紹實在太少了。只說用RAC來處理的話不用removeObserver,但是其實不然。

一個栗子

如果這個介面是被push過來的。 在viewDidLoad中,監聽通知,使用RAC。 然後pop回去,再push進來。。。 結果就是這個通知被新增了多個觀察者,而之前的已經被釋放了。看似沒問題,因為向nil發訊息是沒問題的,但是如果裡面還有單例的訪問,或是像上一篇文章說的,用->運算子的話,就會出現嚴重的問題。 就是說,viewDidLoad走幾次,下次通知觸發的時候就會走幾遍block。

因為RAC的監聽通知的實現原理是:

@implementation NSNotificationCenter (RACSupport)

- (RACSignal *)rac_addObserverForName:(NSString *)notificationName object:(id)object {
	@unsafeify(object);
	return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
		@strongify(object);
		id observer = [self addObserverForName:notificationName object:object queue:nil usingBlock:^(NSNotification *note) {
			[subscriber sendNext:note];
		}];

		return [RACDisposable disposableWithBlock:^{
			[self removeObserver:observer];
		}];
	}] setNameWithFormat:@"-rac_addObserverForName: %@ object: <%@: %p>", notificationName, [object class], object];
}

@end
複製程式碼

就是訂閱機制。subscribeNext就是訂閱。常理之中,如何移除訂閱呢?肯定是呼叫RACDisposable中的dispose方法。注意,[NSNotificationCenter defaultCenter] removeObserver是無效的。 因為我們訂閱的是RAC的signal。

但是在RAC的實現裡面,RACDisposable的確是建立了,卻沒有合適的時候呼叫dispose方法。所以下面的block根本就不會走,根本不會removeObserver!

[RACDisposable disposableWithBlock:^{
    [self removeObserver:observer];
}];
複製程式碼

它return給誰了?另外NSNotificationCenter是個單例,所以它的rac_willDeallocSignal肯定是程式結束的時候才會有,並且它的實現裡面並沒有訂閱rac_willDeallocSignal。

我們想要移除這個觀察者只有一個辦法,就是把這個返回的RACSignal用RACDisposable來接收,在控制器的dealloc中,呼叫它的dispose方法。 但是這樣做,得不償失,這樣控制器就要持有這個返回的結果。倒不如用系統原來的做法了。

RAC固然還是相當強大的,但是在需要監聽通知的時候,不推薦使用RAC!

如果有什麼不同意見,歡迎在評論區留言討論~

相關文章