1. 程式人生 > >ReactiveCocoa 3.0 初窺

ReactiveCocoa 3.0 初窺

本篇博文介紹了 ReactiveCocoa 3.0 (以下簡稱 RC)全新的 Swift 介面,包括泛型,操作符以及柯里化函式的有趣用法。

這是我為 RC3 系列準備的第一篇文章。這篇文章主要的側重點是介紹 Swift 版本的 Signal 類,在下一期文章中我會展示一個更完整的應用。

引言

我成為RC的粉絲已經很久了,Ray Wenderlich 網站上已經有我發表的數篇文章,以及一些與 RC 相關的幻燈片。

在Swift問世之初,我們就可以利用橋接 Objective-C 版的 RC 介面在 Swift 中使用 RC。但是能夠充分利用 Swift 的特性,如泛型等,會使得 Swift RC 更加純粹完美!

感謝 RC 團隊,他們在過去的幾個月裡一直工作在全新的 Swift RC 分支上。就在一週前第一個 beta 版本問世,也就是今天這篇文章的主角。

在閱讀這篇文章我假定您已經有了一定的 RC 基礎,當然,您不用成為一個頂級專家。

建立 SIGNALS

使用 Cathage 可以在專案中非常方便的引入 RC,在工程的根目錄建立一個 Cartfile 檔案引用 RC3:

Objective-C
1 github"ReactiveCocoa/ReactiveCocoa""v3.0-beta.1"

如文件所述,執行 carthage update.

RC3 包含了全新的 Swift API,同時也相容 Objective-C。所以你會發現兩個 Signal 類, Obj-C 版本的 RACSignal 以及 Swift 版本的 Signal

Swift 版本的 Signal 類一個重要特性就是它是泛型的:

Objective-C
123 classSignal<T, E: ErrorType>{...}

型別引數 T 表明 Signal 物件發出的 ‘next’ 事件所附帶的資料型別,錯誤型別由 E 表示,並要求實現 ErrorType 協議。 Swift 的 signals 類與 OC 版本具有類似的用法,下面展示了一個每秒丟擲事件的 signal 類:

Objective-C
12345678910 func createSignal()->Signal<String,NoError>{var count=0returnSignal{sink inNSTimer.schedule(repeatInterval:1.0){timer insendNext(sink,"tick #(count++)")}returnnil}}

Signal 類的初始化方法需要傳入一個生成器,在上例中傳入的是一個閉包。生成器被呼叫後傳入一個 sink 物件,sink 物件的型別是 SinkOf<Event<String, NoError>>。任何傳送給 sink 物件的事件都會被 signal 物件丟擲。

sendNext 函式把區域性變數 count 作為第二個引數,構造了一個事件傳遞給 sink。

Swift Signal 類與 ObjC 版本的 Signal 類有相似的記憶體管理機制,當一個 signal 例項生命週期結束時,需要被釋放。在上例中這一步被包含在閉包表示式之中的最後一步

監聽 SIGNALS

有若干種方法你可以監聽一個 Signal。最簡單的方法是使用 observe 方法,為你所感興趣的事件提供一個函式或者閉包回撥。

這是一個監聽 next 事件的例子:

Objective-C
12 let signal=createSignal()signal.observe(next:{println($0)})

輸出如下:

Objective-C
12345 tick#0tick#1tick#2tick#3tick#4

或者,你可以提供一個 sink 變數監聽 signal 的時間:

Objective-C
123456789 createSignal().observe(SinkOf{event inswitchevent{caselet.Next(data):    println(data.unbox)default:    break}})

Event 型別是一個列舉,關聯了 next 和 error 事件。Sinkof 初始化方法構造了一個 SinkOf<Event<String, NoError>> 型別的sink 變數,同樣的,後面的閉包表示式作為引數傳給了初始化方法。

由於 Swift 語言的限制,Event 型別的列舉(next,error 事件)攜帶的資料被 LlamaKit Box 類封裝在暗盒中,作為一個 RC3 的使用者你很少需要直接跟 Event 型別打交道,有很多 API 可以幫你進行封裝和解封的操作。

上面的例子展示了些許 Swift 跟 RC 結合所帶來的優勢。使用泛型定義 Signal 意味著監聽事件時可以更安全的獲取資料型別,此外,如果你使用的是很複雜的泛型巢狀,你也不用去顯式的宣告泛型了。

轉換 SIGNALS

Swift Signal 型別與它的 Objc 版本具有諸多相似的特性.可是,他們之間有一個根本的區別。

對於一個簡單的map操作,你通常會定義一個Signal的方法,虛擬碼如下:

Objective-C
123 classSignal<T, E: ErrorType>{func map(transform:...)->Signal}

不過,map 函式以及其他能應用於 Signal 的操作實際上都是非成員函式:

Objective-C
12345 classSignal<T, E: ErrorType>{}func map(signal: Signal,transform:...)->Signal

不幸的是把成員函式變成非成員函式,介面看起來將很不協調,比如一個 map 操作緊跟著一個 filter 操作看起來像這樣:

Objective-C
1 let transformedSignal=filter(map(signal,{...}),{...})

幸運的是 RC 從 F# 語言中學來了一個非常時髦的操作符 |>。Swift 中的 map 操作其實是一個柯里化函式:

Objective-C
1234 public func map<T,U,E>(transform: T->U)(signal: Signal<T,E>)->Signal<U,E>{}

在第一次呼叫的時候你提供一個轉換函式,它將會根據你提供的轉換函式返回一個將 Signal 對映到另一個 Signal 的新函式。|> 運算子允許你將 Signal 物件對映到其他型別的操作串聯起來。

Objective-C
1234 public func|><T,E,X>(signal: Signal<T,E>,                      transform: Signal<T,E>->X)->X{returntransform(signal)}

在實際應用中,如果想讓當前 signal 物件丟擲大寫字母事件,你可以用如下的柯里函式對映:

Objective-C
123456789 let signal=createSignal();let upperMapping:(Signal<String,NoError>)->(Signal<String,NoError>)=map({value inreturnvalue.uppercaseString})let newSignal=upperMapping(signal)newSignal.observe(next:{println($0)})

輸出如下

Objective-C
12345 TICK#0TICK#1TICK#2TICK#3TICK#4

注意 upperMapping 常量有一個顯式的型別 (Signal<String, NoError>) -> (Signal<String, NoError>),因為編譯器沒法通過你提供的引數推斷出這個變數的型別。

使用運算子,你可以用如下方式來轉換 Signal

Objective-C
1 let newSignal=signal|>upperMapping

甚至你可以用此運算子來關聯回撥來監聽此 Signal:

Objective-C
123 signal|>upperMapping|>observe(next:{println($0)})

最後,與其將這個柯里函式賦給 upperMapping 常量,你也可以用如下方式將其做關聯

Objective-C
123 signal|>map{$0.uppercaseString}|>observe(next:{println($0)})

注意這樣做你就不用在顯式告知編譯器 map 函式的返回型別了,編譯器可以從上下文中推斷得到。這一切棒極了!最後一點,你可以改變流經這個管道的資料的型別,以及 signal 的型別,如下例所示:

Objective-C
123 signal|>map{count($0)}|>observe(next:{println($0)})

上述的對映操作將 Signal<String, NoError> 變換為 Signal<Int, NoError>。你可以看到 next 時間攜帶的資料型別已經發生了改變:

操作符和柯里函式的使用會花費你不少的精力。由於額外的泛型引數整個函式介面變得更加複雜。為了解釋這些特性如何協同工作我建了一個示例專案,使用了操作符和大量非成員函式。我建了一個包含註釋的 playground,可以在gist上檢視,瞭解更多細節。

總結

RC3 的核心思想和 RC2 完全一致,Signal 產生事件,不過內部的實現卻大相徑庭。雖然你不需要真正去理解 Signal 和操作符的實現機制,但是在你除錯程式的時候會變得非常有用。

當你使用 RC3 時,可能你所面對的編譯錯誤會誤導你,通常情況下並不會定位到正確地位置。

在你查錯的時候把這些 Signal 通道排除是非常有用的,以便你可以定位錯誤的源頭。

你可能會問會什麼要用操作符和柯里函式來構造一個這麼複雜的 API ?當我第一次上手 RC3 的時候也有相同的疑問!

使用非成員函式的一個優點是它們不會受到繼承規則的約束。譬如,Swift 的 foundation 庫定義了一個 map 函式可以將任何實現 CollectionType 協議的物件轉換成任一物件。所以,你可以將這個函式應用到任何集合類,即便和 foundation 類沒有繼承關係。

在我的下篇博文中我將介紹更多 RC 中的新概念,包括 Signal Producer,RC3 中 Cold Signal 的替代方案!

相關推薦

ReactiveCocoa 3.0

本篇博文介紹了 ReactiveCocoa 3.0 (以下簡稱 RC)全新的 Swift 介面,包括泛型,操作符以及柯里化函式的有趣用法。 這是我為 RC3 系列準備的第一篇文章。這篇文章主要的側重點是介紹 Swift 版本的 Signal 類,在下一期文章中我會展示一個更完整

vue-cli 3.0 體驗

img 項目 alt 3.x ini cti eight for clean 一. 準備   1.卸載vue-cli 1.x 或2.x版本,npm uninstall -g vue-cli   2.node版本需 8.9 及以上 二. 安裝   1. 安裝3.x版本 npm

ReactiveCocoa 3.0 初見(2)

在我的前一篇部落格中首次嘗試了ReactiveCocoa3.0 (RC3),在那篇部落格中提到了新的 Signal 介面和 |> 操作符。 在這篇部落格中我將繼續我們探索RC3 API的旅程,我將著重講述訊號發生器,還會從整體上談論一下新 ReactiveCocoa AP

Java for Web學習筆記(一四二)Spring security準備(3

瞭解Spring Security的基本知識 完全J2EE的web container也能提供完整的安全框架,但tomcat不是。Spring Security可以使用JDBC,或者我們的服務或倉庫來認證使用者,也提供了內建的對微軟Active Derectory,Jasi

VUE 3.0 體驗之路

### 碼文不易啊,轉載請帶上本文連結呀,感謝感謝 https://www.cnblogs.com/echoyya/p/14394057.html 在2020年9月中旬,vue.js釋出了3.0正式版,在不久的將來,VUE3.0 也終將成為大前端的必然趨勢, ## 環境搭建 1. `node` 版本要求

Android Studio 3.0 正式版 體驗

blog ice images 安裝 str ext gradle 空間 文件夾 原來的版本是 2.3 ,一開始安裝,詢問是否需要卸載2.3,猶豫了一下,還是卸載了,硬盤空間吃緊…… 然後一路Next,比較順利。 遇到的幾個問題跟安裝2.

Rocket MQ 4.3.0分散式事務訊息

前言 從4.3.0版本開始支援事務訊息,這是一個令人振奮的訊息,線上目前4.2.0,在正式投產使用之前先進性簡單分析。幫助使用者實現類似 X/Open XA 的分佈事務功能,通過 MQ 事務訊息能達到分散式事務的最終一致。 基礎概念 事務訊息:MQ 提供類似 X/Open XA

Hadoop-0.20.2原始碼學習(1)——原始碼

參考: JeffreyZhou的部落格園 《Hadoop權威指南》第四版 0. 為什麼選擇0.20.2版本 前面學習搭建的Hadoop版本是2.7.6,可是這裡為什麼要學習0.20.2這麼老的版本呢?

滲透--IIS6.0解析漏洞

    學滲透一定要動手操作,一個簡單的動作重複N遍以上就能學會了。     最近在學習一句話木馬的使用,加上中國菜刀,真是個居家旅行的必備技能。舉例說asp的一句話: <%execute request(chr(97))%> a就是連線密碼了。(一定要寫對ch

Android框架之路——Retrofit2.0(包含Gson)

參考部落格: 實現效果:        使用姿勢:    1. 使用教程 新增依賴 compile ‘com.squareup.retrofit2:retrofit:2.2.0’ compile ‘com.squareup.retr

ASP.NET Core 3.0 上的gRPC服務模板體驗(多圖)

XML 代碼管理 grpc 文件內容 作者 發送 需要 web 應用 創建 原文:ASP.NET Core 3.0 上的gRPC服務模板初體驗(多圖)早就聽說ASP.NET Core 3.0中引入了gRPC的服務模板,正好趁著家裏電腦剛做了新系統,然後裝了VS2019的功夫

mysql 5.7.3.0-m13安裝教程

com 處理 技術分享 mysql 5.7 bench aid target 驗證 htm 安裝mysql百度經驗地址:(默認安裝,除了選擇不更新和選擇保存路徑,其它基本是下一步下一步) http://jingyan.baidu.com/article/7e4409

Package gtk+-3.0 was not found in the pkg-config search path

path 二進制 all 項目 有時 rpm fedora ack share 問題描述:   在fedora21系統上通過rpmbuild構建fcitx的二進制包時出現以上錯誤,經老程序員指點:“是相應的開發包沒有安裝” 解決辦法:   yum installl gtk3

Yasm 1.3.0 Release Notes

seh st3 require sys storage uil ble ear welcom Yasm 1.3.0 Release Notes http://yasm.tortall.net/releases/Release1.3.0.html Target Audie

Cocos2d-x 3.0 打造一個全平臺概念文件夾

android cocos2d www 全平臺 -s http 概念 1-1 style Cocos2d-x 3.0 打造一個全平臺概念文件夾http:// www.eoeandroid.com/thread-328055-1-1.html Cocos2d

cocos2d-x 3.0 final 移植 android

filename details blank each 連接 python 頭文件 osd 需求 準備工作 你僅僅要依照上一篇的 cocos2d-x 3.0 final 環境搭建 完畢就能夠了 1.編輯proj.android\jni\Android.mk,更改內

Cocos2d-X研究之3.0 場景切換特效匯總

dsm 上下 ble onf color mon 逆時針 radi 下場 ?? cocos2d-x 3.0中場景切換特效比較多。並且遊戲開發中也常常須要用到這些特效。來使場景切換時不至於那麽幹巴,遂這裏匯總一下,開發中使用。 場景切換用到導演類Directory,大多數

12、Cocos2dx 3.0遊戲開發找小三之3.0中的生命周期分析

ide () mil and 地理 splay ioe ase ima 重開發人員的勞動成果。轉載的時候請務必註明出處:http://blog.csdn.net/haomengzhu/article/details/27706303 生命周期分析 在前面文章中我

Android studio 3.0 引起的 outputFile sync failed:not vaild

-m .apk through version 3.0 put 官網 我們 ready 我們大多使用 android studio 改變生成安裝包命名會用以下方式: applicationVariants.all { variant -> vari

[Cocos2d-x v3.x]Mac OX 創建新的Cocos2d-x 3.0 項目

tor cor code none cocos 分享 oid folder left 文章內容來自於: http://cocos2d-x.org/wiki/How_to_Start_A_New_Cocos2D-X_Game Mac OS X 10.9 So