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:
1 | github"ReactiveCocoa/ReactiveCocoa""v3.0-beta.1" |
如文件所述,執行 carthage update
.
RC3 包含了全新的 Swift API,同時也相容 Objective-C。所以你會發現兩個 Signal 類, Obj-C 版本的 RACSignal
以及 Swift 版本的 Signal
。
Swift 版本的 Signal 類一個重要特性就是它是泛型的:
Objective-C123 | classSignal<T, E: ErrorType>{...} |
型別引數 T
表明 Signal 物件發出的 ‘next’ 事件所附帶的資料型別,錯誤型別由 E
表示,並要求實現 ErrorType 協議。 Swift 的 signals 類與 OC 版本具有類似的用法,下面展示了一個每秒丟擲事件的 signal 類:
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-C12 | let signal=createSignal()signal.observe(next:{println($0)}) |
輸出如下:
Objective-C12345 | tick#0tick#1tick#2tick#3tick#4 |
或者,你可以提供一個 sink 變數監聽 signal 的時間:
Objective-C123456789 | 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-C123 | classSignal<T, E: ErrorType>{func map(transform:...)->Signal} |
不過,map 函式以及其他能應用於 Signal 的操作實際上都是非成員函式:
Objective-C12345 | classSignal<T, E: ErrorType>{}func map(signal: Signal,transform:...)->Signal |
不幸的是把成員函式變成非成員函式,介面看起來將很不協調,比如一個 map 操作緊跟著一個 filter 操作看起來像這樣:
Objective-C1 | let transformedSignal=filter(map(signal,{...}),{...}) |
幸運的是 RC 從 F# 語言中學來了一個非常時髦的操作符 |>
。Swift 中的 map 操作其實是一個柯里化函式:
1234 | public func map<T,U,E>(transform: T->U)(signal: Signal<T,E>)->Signal<U,E>{} |
在第一次呼叫的時候你提供一個轉換函式,它將會根據你提供的轉換函式返回一個將 Signal 對映到另一個 Signal 的新函式。|> 運算子允許你將 Signal 物件對映到其他型別的操作串聯起來。
Objective-C1234 | public func|><T,E,X>(signal: Signal<T,E>, transform: Signal<T,E>->X)->X{returntransform(signal)} |
在實際應用中,如果想讓當前 signal 物件丟擲大寫字母事件,你可以用如下的柯里函式對映:
Objective-C123456789 | 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-C12345 | TICK#0TICK#1TICK#2TICK#3TICK#4 |
注意 upperMapping
常量有一個顯式的型別 (Signal<String, NoError>) -> (Signal<String, NoError>),因為編譯器沒法通過你提供的引數推斷出這個變數的型別。
使用運算子,你可以用如下方式來轉換 Signal
Objective-C1 | let newSignal=signal|>upperMapping |
甚至你可以用此運算子來關聯回撥來監聽此 Signal:
Objective-C123 | signal|>upperMapping|>observe(next:{println($0)}) |
最後,與其將這個柯里函式賦給 upperMapping 常量,你也可以用如下方式將其做關聯
Objective-C123 | signal|>map{$0.uppercaseString}|>observe(next:{println($0)}) |
注意這樣做你就不用在顯式告知編譯器 map
函式的返回型別了,編譯器可以從上下文中推斷得到。這一切棒極了!最後一點,你可以改變流經這個管道的資料的型別,以及 signal 的型別,如下例所示:
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