原文連結:
reactivecocoa 是一個架構,它允許你在你的ios程式中使用函數響應式(frp)技術。加上第一部分的講解,你将會學會如何使用信号量(對事件發出資料流)如何替代标準的動作和事件處理邏輯。你也會學到如何轉換、分離群組合這些信号量。
在這裡,也就是第二部分裡,你将會學到更多先進的reactivecocoa特性,包括:
1、另外兩個事件類型:error和completed
2、throttling(節流)
3、threading
4、continuations
5、更多。。。
是時候開始了。
twitter instant
然後重新打開工程即可。(這個時候打開twitterinstant.xcworkspace):
1、twitterinstant:這是你的程式邏輯
2、pods:裡面是包括的三方類庫
運作一下程式,你會看到如下的頁面:

花費一會時間讓你自己熟悉一下整個工程。它就是一個簡單的split viewcontroller app.左邊的是rwsearchformviewcontroller,右邊的是:rwsearchresultsviewcontroller。
自己說:原文簡單介紹了一下該工程,就不在介紹看一下就可以了。
驗證搜尋文本
你第一件要做的事情就是去驗證一下搜尋文本,讓它確定大于兩個字元串。如果你看了第一篇文章,這個将會很簡單。
在rwsearchformviewcontroller.m中添加方法如下:
這就簡單的保證了搜尋的字元串大于兩個字元。寫這個很簡單的邏輯你可能會問:為什麼要分開該方法到工程檔案裡面呢?
目前的邏輯很簡單,但是如果後面這個會更複雜呢?在上面的例子中,你隻需要修改一個地方。此外,上面的寫法讓你的代碼更有表現力,它告訴你為什麼要檢查string的長度。我們應該遵守好的編碼習慣,不是麼?
然後,我們導入頭檔案:
然後在導入該頭檔案的檔案裡面的viewdidload後面寫上如下代碼:
想想這是做什麼呢?上面的代碼:
1、取走搜尋文本框的信号量
2、把它轉換一下:用背景色來預示内容是否可用。
3、然後設定backgroundcolor屬性在subscribenext:的block裡面。
build然後運作我們就會發現當搜尋有效的時候就會是白色,搜尋字元串無效的時候就是黃色。
下面是圖解,這個簡單的反應傳輸看起來如下:
ran_textsignal發出包含目前文本框每次改變内容的next事件。map那個步驟轉換text value,将其轉換成了color,subscribenext那一步将這個value提供給了textfield的background。
當然了,你從第一個教程一定記得這些,對吧?如果你不記得的話,你也許想在這裡停止閱讀,至少讀了整個測試工程。
在添加twitter 搜尋邏輯之前 ,這裡有一些更有趣的話題。
formatting of pipelines
當你正在鑽研格式化的reactivecocoa代碼的時候,普遍接受的慣例就是:每一個操作在一個新行,和所有步驟垂直對齊的。
在下面的圖檔,你會看到更複雜的對齊方式,從上一個教程拿來的圖檔:
這樣你會更容易看到該組成管道的操作。另外,在每個block中用最少的代碼任何超過幾行的都應該拆分出一個私有的方法。
不幸的是,xcode真的不喜歡這種格式類型的代碼,是以你可能需要找到自己調整。
memory management
思考一下你剛才加入到twitterinstant的代碼。你是否想過你剛才建立的管道式如何保留的呢?無疑地,是否是它沒有指派為一個變量或者屬性他就不會有自己的引用計數,注定會消亡呢?
其中一個設計目标就是reactivecocoa允許這種類型的程式設計,這裡管道可以匿名形式。所有你寫過的響應式代碼都應該看起來比較直覺。
為了支援這種模型,reactivecocoa維持和保留自己全局的信号。如果它有一個或者多個subscribers(訂閱者),信号就會活躍。如果所有的訂閱者都移除掉了,信号就會被釋放。想了解更多關于reactivecocoa管理程序,可以參看memory management 文檔。
這就剩下了最後的問題:你如何從一個信号取消訂閱?當一個completed或者error事件之後,訂閱會自動的移除(一會就會學到)。手工的移除将會通過racdisposable.
所有racsignal的訂閱方法都會傳回一個racdisposable執行個體,它允許你通過處置方法手動的移除訂閱。下面是一個使用目前管道的快速的例子。
你不會經常做這些,但是你必須知道可能性的存在。
avoiding retain cycles
當reactivecocoa在場景背後做了好多聰明的事情—這就意味着你不必要擔心太多關于信号量的記憶體管理——這裡有一個很重要的記憶體喜愛那個管的問你你需要考慮。
如果你看到下面的響應式代碼你僅僅加入:
subscribenext:block使用self來獲得一個textfield的引用,blocks在封閉傳回内捕獲并且持有了值。是以在self和這個信号量之間造成了強引用,造成了循環引用。這取決于對象的生命周期,如果他的生命周期是應用程式的生命周期,那這樣是沒關系的,但是在更複雜的應用中就不行了。
為了避免這種潛在的循環引用,蘋果官方文檔:working with blocks 建議捕捉一個弱引用self,目前的代碼可以這樣寫:
在上面的代碼中,bself就是self标記為__weak(使用它可以make一個弱引用)的引用,現在可以看到使用textfield的時候使用bself代用的。這看起來并不是那麼高雅。
reactivecocoa架構包含了一個小訣竅,你可以使用它代替上百年的代碼。添加下面的引用:
@weakify(self)然後代碼修改後如下:
@weakify和@strongify語句是在extended objective-c庫的宏定義,他們也包含在reactivecocoa中。@weakify 宏定義允許你建立一個若飲用的影子變量,@strongify宏定義允許你建立一個前面使用@weakify傳遞的強引用變量。
最後一個提醒,當在blocks使用執行個體變量的時候要小心,這樣也會導緻block捕獲一個self的強引用。你可以打開編譯警告去告訴你你的代碼有這個問題。
好了,你從理論中幸存出來了,恭喜。現在你變得更加明智,準備移步到有趣的環節:添加一些真實的函數到你的工程裡面。
requesting access to twitter
為了在twitterinstant 應用中去搜尋tweets,你将會用到社交架構(social framework)。為了通路twitter你需要使用accounts framework。
在你添加代碼之前,你需要到模拟器中輸入你的賬号:
設定好賬号之後,然後你隻需要在rwsearchformviewcontroller.m中導入以下檔案即可:
然後在引入的頭檔案下面寫如下的代碼:
你将會使用這些簡單地鑒定錯誤。然後在interface和end之間聲明兩個屬性:
acaccountsstore類提供通路你目前裝置有的social賬号,acaccounttype類代表指定類型的賬戶。
然後在viewdidload裡面加入以下代碼:
這些代碼建立了賬戶存儲和twitter賬号标示。在.m中添加如下方法:
這個方法的作用是:
1、定義了如果使用者拒絕通路的錯誤
2、根據第一個入門教程,類方法createsignal傳回了一個racsignal的執行個體。
3、通過賬戶存儲請求通路twitter。在這一點上,使用者将看到一個提示,要求他們給予這個程式通路twitter賬戶的彈框。
4、當使用者同意或者拒絕通路,信号事件就會觸發。如果使用者同意通路,next事件将會緊随而來,然後是completed發送,如果使用者拒絕通路,error事件會觸發。
如果你回想其第一個入門教程,一個信号可以以三種不同的事件發出:
1、next
2、completed
3、error
超過了signal的生命周期,它将不會發出任何信号事件。
最後,為了充分利用信号,在viewdidload後面添加如下代碼;
如果你運作程式,将會看到一個彈出框:
提示是否允許通路權限,如果ok,則列印出來access granted ,否則将會走error。
accounts framework會記住你的決定,是以如果想再次測試,你需要針對模拟機進行:reset contents and settings。
chaining signals
一旦使用者允許通路twitter賬戶,為了執行twitter,程式将會不斷監聽搜尋内容textfield的變化.
程式需要等待信号,它請求通路twitter去發出completed事件,然後訂閱textfield的信号。不同信号連續的鍊是一個共有的問題,但是reactivecocoa處理起來非常優雅。
用下面的代碼替換目前在viewdidload後面的管道:
then方法會一直等待,知道completed事件發出,然後訂閱者通過自己的block參數傳回,這有效地将控制從一個信号傳遞給下一個。
then方法傳遞error事件。是以最後的subscribenext:error: block還接收初始的通路請求錯誤。
當你運作的時候,然後允許通路,你應該可以在控制台看到列印出來的你輸入的東西。
然後,添加filter操作到管道去移除無效的搜尋字元串。在這個執行個體中,他們是不到三個字元的string:
運作就可以在控制台看到隻有三個以上的才能輸出。
圖解一下上邊的管道:
程式管道從requestaccesstotwittersignal信号開始,然後轉換到tac_textsignal。同僚next事件通過filter,最後到達訂閱block.你也可以看到任何通過第一步的error事件。
現在你有一個發出搜尋text的信号,它可以用來搜尋twitter了。很有趣吧。
searching twitter
social framework是一個通路twitter 搜尋api的選項。然而,它并無法響應搜尋,下一步就是給信号包括api請求方法。在目前的控制器中,添加如下方法:
下一步就是建立一個基于request的信号量。添加如下方法:這建立了一個請求:搜尋twitter(v.1.1rest api)。這個是調用twitter的api。
然後在viewdidload方法中進一步添加信号量:
運作:
即可在控制台裡面列印出來篩選的資料。
threading
我很确信你這會亟待把json資料放到ui裡面,但是在放到ui裡面之前你需要做最後一件事:找到他是什麼,你需要做一些探索!
添加一個端點到subscribenext:error:那個步,然後我們會看到xcode左側的thread,我們發現如果想加載圖檔的話必須在主線程裡面,但是他不在主線程中,是以我們就可以做如下操作:
這樣就會在主線程中運作。也就是更新了管道:添加了deliveron:操作。
然後再次運作我們就會發現他是在主線程上執行了。這樣你就可以更新ui了。
updating the ui
我們在rwsearchformviewcontroller中導入:
然後在輸出json資料的地方修改如下:
就可以看到右側的詳情頁面加載到資料了。剛引入的類庫其實就是将json資料轉換成了model.加載資料的效果如下:
asynchronous loading of images
現在内容都加載出來了,就差圖檔了。在rwsearchresultsviewcontroller.m中添加如下方法:
這會你一ing該就會很熟悉這種模式了。然後在tableview:cellforrowatindex:方法裡面添加:
再次運作就可以出來效果了:
throttling(限流)
你可能注意到這個問題:每次輸入一個字元串都會立即執行然後導緻重新整理太快 ,導緻每秒會顯示幾次搜尋結果。這不是理想的狀态。
一個好的解決方式就是如果搜尋内容不變之後的時間間隔後在搜尋比如500毫秒。
而reactivecocoa是這個工作變的如此簡單。
打開rwsearchformviewcontroller.m然後更新管道,調整如下:
你會發現這樣就可以了。throttle操作隻是發送一個操作,這個操作在時間到之後繼續進行。
wrap up
現在我們知道reactivecocoa是多麼的優雅。