談談ReactiveCocoa

ReactiveCocoaGithub開源的一款cocoa FRP 框架。react

Native app有很大一部分的時間是在等待事件發生,而後響應事件,好比等待網絡請求完成,等待用戶的操做,等待某些狀態值的改變等等,等這些事件發生後,再作進一步處理。 可是這些等待和響應,並無一個統一的處理方式。Delegate, Notification, Block, KVO, 經常會不知道該用哪一個最合適。有時須要chain或者compose某幾個事件,就須要多個狀態變量,而狀態變量一多,複雜度也就上來了。爲了解決這些問題,Github的工程師們開發了ReactiveCocoa。git

 

幾個常見的概念
在閱讀ReactiveCocoa(如下簡稱RAC)的相關文章或代碼時,常常會出現一些名詞,理解它們對於理解RAC有很大的幫助,下面就簡要來講說這些常見的概念。
 
Signal and Subscriber
這是RAC最核心的內容,這裏我想用插頭和插座來描述,插座是Signal,插頭是Subscriber。想象某個遙遠的星球,他們的電像某種物質同樣被集中存儲,且很珍貴。插座負責去獲取電,插頭負責使用電,並且一個插座能夠插任意數量的插頭。當一個插座(Signal)沒有插頭(Subscriber)時什麼也不幹,也就是處於冷(Cold)的狀態,只有插了插頭時纔會去獲取,這個時候就處於熱(Hot)的狀態。
 
Signal獲取到數據後,會調用Subscriber的sendNext, sendComplete, sendError方法來傳送數據給Subscriber,Subscriber天然也有方法來獲取傳過來的數據,如:[signal subscribeNext:error:completed]。這樣只要沒有sendComplete和sendError,新的值就會經過sendNext源源不斷地傳送過來,舉個簡單的例子:
 
  1. [RACObserve(self, username) subscribeNext: ^(NSString *newName){ 
  2.     NSLog(@"newName:%@", newName); 
  3. }]; 
 
RACObserve使用了KVO來監聽property的變化,只要username被本身或外部改變,block就會被執行。但不是全部的property均可以被RACObserve,該property必須支持KVO,好比NSURLCache的currentDiskUsage就不能被RACObserve。
 
Signal是很靈活的,它能夠被修改(map),過濾(filter),疊加(combine),串聯(chain),這有助於應對更加複雜的狀況,好比:
  1. RAC(self.logInButton, enabled) = [RACSignal 
  2.         combineLatest:@[ 
  3.             self.usernameTextField.rac_textSignal, 
  4.             self.passwordTextField.rac_textSignal, 
  5.             RACObserve(LoginManager.sharedManager, loggingIn), 
  6.             RACObserve(self, loggedIn) 
  7.         ] reduce:^(NSString *username, NSString *password, NSNumber *loggingIn, NSNumber *loggedIn) { 
  8.             return @(username.length > 0 && password.length > 0 && !loggingIn.boolValue && !loggedIn.boolValue); 
  9.         }]; 
 
這段代碼看起來有點複雜,來細細說一下,首先是左邊的RAC(...),它的做用是將self.logInButton.enabled屬性與右邊的signal的sendNext值綁定。也就是若是右邊的reduce的返回值爲NO,那麼enabled就爲NO。右邊的combineLatest是獲取這4個signal的next值。其中能夠看到self.usernameTextField.rac_textSignal這麼個東東,rac_textSignal是RAC爲UITextField添加的category,只要usernameTextField的值有變化,這個值就會被返回(sendNext)。combineLatest須要每一個signal至少都有過一次sendNext。reduce的做用是根據接收到的值,再返回一個新的值,這裏是@(YES)和@(NO),必須是object。
 
上面這段代碼用到了Signal的組合,想象一下,若是是傳統的方式,寫起來仍是挺複雜的,並且隨着功能的增長,調整起來會更加麻煩。
 
冷信號(Cold)和熱信號(Hot)
上面提到過這兩個概念,冷信號默認什麼也不幹,好比下面這段代碼
  1. RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) { 
  2.     NSLog(@"triggered"); 
  3.     [subscriber sendNext:@"foobar"]; 
  4.     [subscriber sendCompleted]; 
  5.     return nil; 
  6. }]; 
 
咱們建立了一個Signal,但由於沒有被subscribe,因此什麼也不會發生。加了下面這段代碼後,signal就處於Hot的狀態了,block裏的代碼就會被執行。
  1. [signal subscribeCompleted:^{ 
  2.     NSLog(@"subscription %u", subscriptions); 
  3. }]; 
 
或許你會問,那若是這時又有一個新的subscriber了,signal的block還會被執行嗎?這就牽扯到了另外一個概念:Side Effect
 
Side Effect
仍是上面那段代碼,若是有多個subscriber,那麼signal就會又一次被觸發,控制檯裏會輸出兩次triggered。這或許是你想要的,或許不是。若是要避免這種狀況的發生,可使用 replay 方法,它的做用是保證signal只被觸發一次,而後把sendNext的value存起來,下次再有新的subscriber時,直接發送緩存的數據。
相關文章
相關標籤/搜索