目前Json已經可以說是Internet上相當流行的格式了, 雖然說他缺點還算蠻多的, 但很多主流的程式語言都有成熟的parser可供使用,  在Golang則可利用內建的encoding/json這個package來達成

用“encoding/json”來解析Json其實很簡單, 基本上就是建立一個Decoder然後把json內容”解”到你對應的資料結構去, 可以參考這範例: http://golang.org/pkg/encoding/json/#example_Decoder

但在某些狀況下並不是很好的解法, 舉個例子來說, 我這兩天打算拿從Parse那邊匯出的使用者資料來做一些處理, Parse輸出給我的JSON檔大約長得像這樣: 

{
       results: [
             {
                  “objectId”:”zzreueaWq4″,
                  “username”:”julian”,
             }
       ]
}

按照這定義寫出的程式碼就類似這樣:

但這有一個很大的問題, Parse那邊輸出給我的Json檔就有400MB之多(二十多萬筆), 可想而知的, 這程式直接先因為out of memory炸掉了, Decode需要把所有內容載入到記憶體才做解析, 而且以這範例, 解析完的結果全部存到我們的資料結構內, 自然有這問題, 常常這種應用我們都不是很需要把所有內容在記憶體放一份, 而是來一個處理一個

這時候就需要用Streaming parser的作法來解決這問題了, Golang的Json package也有支援這一模式, 但這也是在最新的1.5之後才有支援這模式, 使用這方法, 程式可以改成如下:

在這邊由於我只需要results這陣列的內容而已, 因此我先用Token找到第一個”[”(以這範例來說, 第一個”[”代表的就是results的陣列了), 之後就是用迴圈一個個去解些個別的User物件, 由於這20多萬筆資料, 我也只是要印出來而已, 我也不用特地用一個大的資料結構來存, 自然記憶體的問題就比較沒那麼吃緊

想說既然打定主意想玩一下iOS上的開發, 就從一些最夯的東西開始, 使用Swift自是不用說(另一個原因是我還是不喜歡看Objective-C), 另外想說最近FRP (Functional Reactive Programming), 搭配Swift也剛剛好, 因此想說就從ReactiveCocoa來上手吧….

似乎在iOS流行著用CocoaPods來做相依性管理(dependency management), 如果用Objective-C, ReactiveCocoa 2.x版就已經夠用了, 用CocoaPads也非常簡單沒啥問題, 只要在Podfile裡加入即可:

pod ‘ReactiveCocoa’, ’~> 2.4.4’

但根據下面這篇

“MVVM, SWIFT AND REACTIVECOCOA - IT’S ALL GOOD!”

但2.x跟swift不搭,如果要在Swift上使用2.x版的ReactiveCocoa,就必須要使用這個分支 但使用這個分支的話, 就沒辦法透過CococaPods來管理了

不過所幸, ReactiveCocoa打算在3.0之後直接支援swift, 那如果, 把pod改成:

pod 'ReactiveCocoa’, ’~> 3.0’

不就應該搞定了嘛?代誌當然不是我這笨蛋想的那麼簡單, 首先, 把這些library含進swift project裡要使用dynamic frameworks而不是static library, 這在iOS是iOS8之後才支援的, 而CocoaPods要到0.36才有支援, 針對這個, 就必須要在Podfile裡加上一行

use_frameworks!

但…這樣還沒結束, CocoaPods的repository裡的ReactiveCocoa 3的版本其實只有到 3.0.0 alpha 1, 那是相當早期的版本, swift版本的ReactiveCocoa正在積極開發中, 因此這版本舊到如果是用最新的XCode是會編譯不過的, 問題會出在LlamaKit, LlamaKit版本在CocoaPods上是0.1.0, 但最新的則是0.6.0, 在最新版已經解掉這問題了

那這樣表示我得放棄方便相依管理系統轉向手動了嗎? 還好還有Carthage, 這個跟ReactiveCocoa都是同一個作者做的(好像是Github的員工), Carthage跟CocoaPods的概念不同, CocoaPods的採取的是傳統的中心化的管理, 但Carthage的套件則沒有中心的repository, 而是分散在各個git(github project)中, 相對來說比較簡單單純(感覺就像做git clone後再用xcodebuild去build成framework而已), 不過我在這反而碰到更多的陷阱

像CocoaPods的Podfile一樣, Carthage也有一個設定檔Cartfile, Cartfile的內容跟Podfile很像, 像是要加入ReactiveCocoa 3的話只要加入一行:

github “ReactiveCocoa/ReactiveCocoa” ~> 3.0

這邊跟CocoaPods不一樣的地方是, 它的版本管理部分完全靠git, 因此這行它會去github抓tag是v3.0的最新版本(以現在來說最新的是v3.0-beta.1), 它也可以接某個git branch, 比如說github “ReactiveCocoa/ReactiveCocoa” “swift-development”, 寫好Cartfile後只要執行:

carthage update

它應該…理論上啦…就會把相關的porject抓回來build成framework, 然後去General settings把這些framework”手動”含進來(它不像CocoaPod那麼自動):

不過, 我為什麼說"理論上", 原因是我又碰到問題了!在build到LlamaKit(又是它)時我發生以下的問題!

“project has no shared schemes” error

所以ReactiveCocoa並沒被build出來(X的)

看來幾個對這issue的討論, 似乎是執行xcodebuild -list過久timeout, 導致抓不到build target, 而根據commit的log, 0.6.5似乎解決了這問題, 我原本是用brew install carthage裝的, 所以版本是0.6.4, 所以有抓了package來裝0.6.5

裝了0.6.5就解決了嗎?我這笨蛋真的想的太簡單啦!一樣有問題啦!…沒解嗎? 開了那個commit來看, 原來它所謂的解不過是延長timeout並且把錯誤訊息改成讓人家知道那是因為timeout而不是沒有shared scheme

那怎辦, 該回歸手動了嗎?不!要追根究柢!,但看半天好像也沒啥招, 死馬當活馬醫? cd進去Carthage/Checkouts/ReactiveCocoa/目錄, 發現也有Cartfile, 那就在這邊也來一次carthage update吧….結果有沒問題?有!狀況出現在底下這兩行(Cartfile.private):

github “Quick/Quick” “master” github “Quick/Nimble” “master”

這次它說沒"master"這個object, 但carthage不是有支援用branch name嗎?不管, 拿掉試試, 把"master"都拿掉後, 就成功在這邊build過ReactiveCocoa, 那回到上一層還會出現xcode -list發生timeout的問題嗎?回到上層目錄再試, 正常了, 一次通過! X…該不會是這問題吧?這是什麼情形….那我把github “ReactiveCocoa/ReactiveCocoa” ~> 3.0 改成github “ReactiveCocoa/ReactiveCocoa” “swift-development"呢?果不期然!!一樣的問題!!不是應該有支援用branch name嗎?

(寫太多很累了, 省略發現原因的過程…底下直接寫原因)


原因是我電腦裡面有兩個git, 一個是git 1.7的版本, 一個是macos 10.10幫我裝的git, 那個則是2.3.2的, 因為路徑設的問題, 一直都抓到1.7的版本, carthage認branch name是靠git revparse, 大概1.7跟carthage不相容, 導致了這問題, 改成2.3.2之後….一路順暢….看來來去回報一下好了

但問題不是到這邊為止, carthage還是一個很新的東西, 還有一些像是Facebook/pop並不支援carthage, 現在還沒辦法全用carthage管理, 還好CocoaPods跟Carthage是可以混用的, 所以接下來應該可以讓我順利開始寫東西了吧(希望如此)

後記補充


經多次實驗, Shared scheme跟no object的確是兩個不同的問題, 前者0.6.5的確解決了這問題(我猜是我mac mini太慢所以timeout延到8s還不夠才又碰到, MBPR則沒這問題), 後者的確是git版本造成的