Core Data 線程大揭秘

jopen 8年前發布 | 22K 次閱讀 線程 Core Data iOS開發 移動開發

到了今天,Core Data 中的線程實現機制已經與其最初版本大相徑庭了(也就是 iOS 6 之前的版本)。在 Core Data 的悠久歷史中,多年來關于如何使用線程這個話題已經有了數種不同的解釋,那么我們到底應該怎么做呢?在本次 #Pragma Conference 2015 講演中,Marcus Zarra 為我們展示了實現線程的三種方法,舊有的、復雜的以及最佳的。

See the discussion on Hacker News .

Transcription below provided by Realm: a replacement for SQLite & Core Data that makes building iOS apps a joy. Check out the docs!

</div>

Sign up to be notified of new videos — we won’t email you for any other reason, ever.

About the Speaker: Marcus Zarra

Marcus Zarra 最為出名的就是他精通 Core Data、持久化存儲以及網絡等相關知識。他從 2004 年開始開發 Cocoa 應用,絕大多數時間他都用在開發軟件上面。Marcus 是 Core Data: Apple’s API for Persisting Data under Mac OS X 的作者,還是 Core Animation: Simplified Animation Techniques for Mac and iPhone Development 的作者之一,此外還是 [Cocoa Is My Girlfriend] 博客的作者之一。

</div>

@mzarra

</div>

概述(0:00)

本次演講的目標是讓大家清楚,在 2015 年的今天,我們應該如何正確地使用 Core Data。我們首先以我的基本準則開始,也就是當使用框架或者 Core Data 時候,如何正確地使用編譯閉包(building blocks)。接著,我會在一個多線程環境下和大家討論使用 Core Data 的舊有方式、復雜方式以及最佳方式。最后,我會總結一些指南,以及 Core Data 中一些復雜情況的注意事項。

Core Data 已經存在了 11 個年頭了,但是對我來說它仍舊是一個新事物,我依舊孜孜不倦地汲取其中的知識。對于一個框架來說,11 年確實是一個很長的時間了,尤其是處于移動化趨勢的今天。使用老框架的其中一個問題是,11 年來一直都有關于 Core Data 的博客和文章發布出來,然而其中的某些已經失效了,存在著許多沖突的信息。

為什么(不)用線程呢?(3:16)

在我使用線程之前,我都會問自己:“這是否有必要?”。經常有人過來跟我說:“好吧,我們遇到了一個關于 Core Data 的問題。我們向 Core Data 應用中添加了線程,現在我們被逼入死角了。”我會反問他們你們為什么要添加線程呢?這聽起來似乎答非所問,但是我總是會這樣做,因為大家總是做錯了!人們的回答通常是為了解決性能問題。然而,使用線程來解決一個性能問題通常會讓其變成兩個、四個、八個甚至更多的性能問題。

線程不是萬能的。往應用中添加線程取決于架構的設計,我們如果是在發布前的幾個小時中才做出的這個決定,那么就大錯特錯了。向應用中添加線程應該是在我們發現 CPU 有空余的時間和帶寬的時候才進行的,或者是當我們想要預測用戶的下一步動作并提前進行準備的時候才使用線程。

其中一個例子就是 推ter 應用,也是我最喜歡的例子之一。當你登錄到 推ter 的時候,我們需要使用線程來獲取用戶還未看到的圖像。此外我們還可能會緩存頭像文件、獲取檢索結果等等。我們需要使用線程來預測用戶將要做什么,然后提前為他們準備好數據,這樣當用戶使用的時候,就會驚嘆訪問應用的速度何其迅速。其實實際上并不是訪問速度夠快,我們只是在后臺提前做好了一切準備。

我們在后臺為用戶做好準備,這樣用戶就無需進行等待。比如說發 推ter 這個操作,用戶根本無需等待網絡請求返回。我們不需要在屏幕上放一個提示框,告訴用戶:“嘿,我的時間比你的時間更為寶貴。你就等在那,如果 推ter 發送成功了我會告訴你的。在此之前,你不能做其他事情。”多么差的用戶體驗,對吧?線程便可以解決這些極差的用戶體驗。

Core Data 中的線程:核心概念(6:43)

關鍵是保證數據源唯一

當我們在使用 Core Data 的時候,我們通常會使用一個或多個 NSManagedObjectContext ,這其中只能有一個作為數據源,也就是用戶界面唯一能夠訪問到的。用戶界面是我們的關鍵所在。我們開發的應用主要是面向用戶的,而不是面向服務器的。我們在 iPhone、iPad 以及個人電腦上開發這些應用。用戶應該隨時隨地都能夠看到界面,得到反饋。因此,我們只能使用一個上下文 (context) 專門與用戶進行交互,以及為用戶展示界面元素。

“關鍵是保證數據源唯一(Single Source of Truth)”是一個專業術語,意思是“這個數據源作為核心存在,每當我們需要確認哪幾種可能性是正確的時候,這個數據源提供的數據始終都是正確的”。

UI 線程也是核心所在

所有的用戶界面都是單線程的。我使用過的所有語言中,它們的用戶界面都是運行在單線程上的。如果你打算在后臺線程中顯示一個 UIViewController 的話,這可能會導致錯誤的發生。由于 UI 是單線程上運行的,因此我們應該使用唯一的數據源來構建 UI。

非用戶控制的數據不應該在主線程上進行操作

如果我們不對 UI 進行控制的話,那么我們就不應該在 UI 線程上進行數據操作。在解析 JSON 的時候,我們應該避免在 UI 線程上進行。在上傳數據到服務器上的時候,無論是 推ter 消息還是銀行交易信息,我們也不應該在主線程上進行。UI 線程僅供用戶界面的操作,其他都一邊去。

堅守這三條原則就可以解決大多數 Core Data 中出現的性能問題。無論我們在哪個系統中使用 Core Data,我們都會規避掉大多數性能問題。

舊有方式(9:34)

所謂的“舊有”方式,實質上指的是最原始的 Core Data 實現方式,也就是我們在 iOS 6 之前實現 Core Data 線程的方式。蘋果的編程框架在 iOS 5 中搞了一個相當大的觀念轉變。在我看來,這導致大量開發者涌入 iOS 開發當中來。在 iPhone 變成街機之前,你或許可以用一只手將 Objective-C 開發者數過來。我們都有著相同的郵件列表,我們互相都認識,我們甚至還認識在蘋果里面工作的那些工程師。蘋果會發布一些很激進、變化很大的框架,而且不會告訴你它們的使用方式,因為他們知道我們會去自行慢慢探索。接著我們會對問題進行討論,這完全是因為這個圈子實在太小了。線程的情況也和這類似,線程的規則不是很清晰,每個人都有各自的看法。因為這完全是一個全新的技術。

接著,突然之間一下子涌入了成千上萬名開發者。在 2006 到 2008 年間,我覺得開發者的數目從 近萬名變成了近百萬名。我記得在 WWDC 上,整個會場都充斥著一隊隊人,討論著“好的,如果你使用了 retain,它會將保留計數 (retain count) 減1。當你釋放的時候,保留計數還會再減1。當它變成0的時候,這個對象就被銷毀了,而如果它變成了-1的話,就會發生崩潰。”他們需要一遍又一遍的重復這個話題,因為這個概念十分難以理解。新加入 iOS 開發的很多人對 Objective-C 并不十分擅長,因此他們總是問“所以說我們必須要手動管理內存咯?”之類的問題。這是因為這個概念它們之前從未接觸過。

因此,蘋果必須要弄出一個范例來告訴大家如何使用這些框架和 API。其中之一就是線程,因為線程實在實在太難了。我們所有人都曾犯過錯誤,即使是蘋果內部的開發人員也是如此。在這里,這是一個很難解決的問題。提供的范例建議大家使用隊列 (queue) 而不是使用線程。如果你做的對的話,除非你將兩者混合使用,不然的話使用隊列和線程是沒有太大區別的。隊列十分好用,因為它們的工作效果和線程一樣,即使我們寫了錯誤的代碼,它們仍能保護好程序現場,這的確是一個很酷的功能。

在 iOS 5 中,Core Data 發生了變化。它們展示了 ManagedObjectContext(MOC) 的三種類型,我們可以選一種類型來使用。這樣做的目的是為了讓線程使用更加簡單,但是這在 iOS 5 中并不是那么好用。它十分古怪,并且還有點不穩定。在 iOS 6 中,蘋果解決了問題,讓其變得更加穩定,因此在 iOS 6 上它們才能切實的工作。

譯者注:此處用的是圖例,沒有代碼。如果希望查看 Keynote 的話,請KX上網觀看。

</div>

在 iOS 6 之前,這就是我們使用 Core Data 的方式,它現在仍然能夠使用,除非為了處理某些邊緣案例,否則的話基本上是不會有人想使用它的。我們會以一個 PersistentStoreCoordinator(PSC) 開始,它用以處理與硬盤的所有交互動作。它一般用來從硬盤取得數據,將其轉換為對象模型以及將其放回硬盤。應用中只使用一個 PSC 即可。

每次我們創建 MOC 的時候,它都會直接與 PSC 進行通信。所有的 MOC 都會與 PSC 進行交互,但是MOC 之間無法進行溝通。當我們想要讓某個上下文知道另一個在其他線程上的上下文發生的變化,就十分困難。我們必須通過通知系統 (notification system) 來處理這個問題,因為每次我們對 MOC 進行改變的時候,都會廣播一個通知,這樣我們就可以捕獲這個通知,然后在另一個上下文中對其進行處理。我們必須正確地使用這個功能。

這會導致一系列問題發生,因為這個系統已經優化了好幾年了。當 Core Data 第一次推出的時候,每個人都有自己的看法。然而這幾年即使越來越多的規則出臺,人們仍然還是十分困惑它的用法。

在這個系統中,我們通常使用一個唯一數據源來與用戶界面溝通,也就是核心 MOC,我們讓其將變化反饋給用戶界面。我們并沒有別的辦法來特別定義它,因為只有一種定義上下文的方式。用戶界面不會在其它線程中出現,因為它是單線程的。

使用舊有方法會出現的問題(16:26)

這種設計導致的問題就是海量的代碼。以前人們總是抱怨 Core Data 非常難用,并且在現在這個年代還要寫這么多樣板代碼簡直就是反人類。這么做會導致在你代碼的末尾,可能會散落著大量的 Core Data 代碼。

線程規則同樣也不是很清晰。在 iOS 6 之前,如果你在網上詢問如何處理 Core Data 中的線程的話,每一個人都會有不同的回答。這些回答可能有“將上下文進行鎖定,然后同步使用就萬事大吉了”,這實際上并不是很好用。接著,還會有“為每一個線程準備一個上下文,從某個上下文中出來的數據都只在那個線程中使用。你可以跨線程讀取數據,但是切記不要寫數據。”然后,就是“跨線程讀取數據也是不可取的,只能夠在單個線程中進行數據的讀寫,保持數據的線程唯一性和孤立性。”然后人們就會發現除非你遵循數據孤立 (siloing) 原則,否則的話操作就是非線程安全的。

線程的規則十分混亂。我們沒有辦法來驗證或者決定哪個規則是完全正確的。你或許會想可以在代碼復查的時候提供不同的參數。誰知道這么做什么時候會發生崩潰呢?就算應用崩潰了,你仍然無法確認崩潰原因,因為很可能并不是那段代碼導致的崩潰。線程這種東西,很難說。

此外,我們有些時候會在錯誤的時間阻塞了錯誤的線程,然后整個應用就崩潰了,然而你并不知道到底發生了什么。或許有人會給其他線程中進行通知的監聽,然后在錯誤的線程中使用的時候對其進行捕獲;也有可能當 UI 做了一些詭異的操作后,然后整個應用就崩了。你會將整個應用中布滿 NSLog,試圖捕獲錯誤,以找出導致錯誤的問題。

當你開始監聽通知的時候,通知會變得十分活躍。當你開始這么做的時候,你或許會有點急于求成,然后開始在所有地方對通知進行監聽。這會影響性能,進而導致意外的線程死鎖,然后你就陷入了死循環,做著無用功。你會發現絕大多數情況下應用都好好的,只有偶爾才會發生崩潰,你試圖找到崩潰的原因……于是你已經陷入了死胡同。

好消息是在 iOS 9 中,我們現在擁有了調試標記,這允許我們確定在使用的線程是正確的。我們現在至少能夠確認這個線程是否工作正常了。

舊有的實現方式非常困難,而且容易出錯。

復雜的方式(21:53)

就我個人而言,我很喜歡使用這種復雜的方式,因為它很有極客風范,不過我并不希望經常在產品中看到這樣的使用方式。

SQLite 是我們經常使用的持久層 (persistence level) 技術。我們用它向硬盤上存儲東西。它被設計為準許多進程訪問 (multi-process access),因此我們可以使用多個 PSC 與之相聯。我們可以同時使用多個線程與之進行溝通。

如果我們有多個 PSC 的話,每個 PSC 都可能會有多個 MOC 與之關聯。現在,我們突然就不需要擔心死鎖的問題了,也不用害怕線程或上下文之間的阻塞問題了。我們可以開心地從一端推送數據,然后從另一端接收這些數據。

我們愈來愈接近真正的 PSC 異步了。當然,這仍然還會存有許多阻塞的問題,即使您通過了測試,您還是有可能遇上阻塞的問題。原因就在于大多數 PSC 所做的工作都是在 CPU 當中完成的。它在內存中獲取我們的對象,然后將它們放入 SQLite 調用語句當中,以此來準備與 SQLite 進行對話。這就是存儲、檢索以及讀取操作所發生的地方。接著在一部分周期 (silver) 中與數據庫建立連接。在這個周期當中,如果您訪問了相同的表以及記錄,并且同時對其進行操作的話,你就會陷入到死鎖當中。這并不是百分百的異步操作,不過99.99%的情況它都有效,每年它的成功率都在上升,遇見死鎖變得越來越困難。好消息是我們可以越來越接近所謂真實的異步。

即使在設計當中,我們仍希望用一個上下文來與用戶界面關聯。我們不希望使用多個上下文。我們就讓其存在一個線程當中,和用戶界面建立關聯即可。

使用復雜方式的問題(24:34)

最大的問題就是這個方法實在實在太難了。除非你試圖解決一個非常特殊的問題,否則我不建議您使用這個方法。這個方法對于我們來說很難完全做對,因為線程本來就很難了,但是在這里我們竟然還要給線程上添加其他層級。之所以這么做是因為 PSC 之間不能互相交流。至少當我們擁有多個 MOC 的時候,我們會與單個 PSC 進行交流,這個 PSC 就會知道發生了什么。上下文就可以根據此執行查詢或者從中獲取一些信息。我們甚至沒法讓多個 PSC 同時訪問單個 SQLite 文件。其中一個 PSC 用以寫入文件,其他用以讀取,它們之間無法溝通交流。不過我們可以通過同步很簡單地取出數據。

在這個系統中通知也變得十分難以實現。不過在 iOS 9 中發生了很大變化,它們添加了一個新特性,允許我們響應遠程通知。然而,您仍然不能從一個 PSC 中發送通知,然后在其他 PSC 中接收通知,因為沒有辦法執行這種操作。我們需要做很多處理才能讓一切順利。

相比第一個版本來說,線程的行為也變得更加詭異。在這個設計中,我們發現了成千上萬條線程問題,因為我們必須確保我們的 PSC 和 MOC 都能夠在正確的線程中相互溝通。

可維護性更是想都別想。當我們被問題卡住的時候我們會采用原始的方式,然后為其添加更多的復雜層面 (layer of complexity)。

我們為什么要采取這個方式呢?通過 watchOS,我們可以借此來獲得多個進程訪問的能力,比如說兩個進程同時訪問相同的 SQLite 文件。隨著我們的觀察,我們可能需要讓多個應用都能夠訪問同一個 SQlite 文件。我們非常需要這個功能,因為越來越多的應用實際上都只訪問一個相同的 SQLite 文件了。我能夠很好地理解這個觀念,因為這也是 iCloud 與 Core Data 協同工作的方式。于是您就可以理解為什么 iCloud 能夠如此之快的變得好用和穩定。

最佳方式(27:47)

最后就是我們處理線程的“最佳”方式。所謂最佳,指的并不是最快。如果你在尋找快速的持久化引擎的話,您不應當使用對象驅動的引擎。你不是在尋找 Objective-C、Core Data 之類的替代品。面向對象編程是非常慢的。如果你需要速度的話,你需要使用 C 或者 SQLQite 這種更底層的東西。通常情況下,最快的代碼往往是這個世界上最丑、最令人討厭的了。

我已經寫過一些運行起來很迅速的引擎,但是我并不會為它們而驕傲。因為它們太丑了。代碼中存有很多很多的注釋,因為不這么搞的話六個月后我就完全看不懂了。

在我看來,“最佳”的就是最容易用的,同時也是最容易維護的。我可以用一杯咖啡的時間來閱讀并理解這些代碼。這些代碼非常簡單,我無需使用白盒測試就可以找到 BUG 所在。

如果一個代碼難以調試的話,那么就沒有人愿意為這個代碼編寫邊緣測試了。如果六個月后我自己寫的代碼都看不懂的話,我想所有人都會瘋的吧,你必須要從頭開始。

在最佳的方式中,我們只用一個 PSC,但是我們要使用 iOS 6 中所提供的新 API。我們打算添加一個與 PSC 進行通話的私有 MOC。接著,我們添加并定義核心上下文,然后讓其成為私有 MOC 的一部分。所有的數據操作都會在核心 MOC 下面進行,因此我們將會擁有三個級別的上下文。

除非我們定義了一個新的核心上下文,否則核心上下文不會發生改變。我們讓其與 UI 進行關聯。無論是哪一個核心上下文,都只能在 UI 線程上使用。如果我們試圖在其他線程中使用它的話,我們就會有一個調試標記,發生崩潰,這樣我們就知道哪出問題了。

這種設計允許我們異步保存,這個功能非常重要。它允許我們不必阻塞 UI 就可以存儲并訪問數據。用戶在滑動我們應用的時候就會非常開心,因為不會有卡頓的情況,也不必讓用戶等待。對我們來說這么做的代碼也非常少,八行代碼即可搞定。

使用最佳方式的問題(31:47)

我承認,找這個方式可能會帶來的問題實在讓我江郎才盡了,因為目前最正確的方式,因此它并不存在很多問題。

你在網上看到的最大問題就是它的速度比較慢。在核心 MOC 與 PSC 之間加入了一個額外的間接層,因此這里我們的訪問速度會被減慢一些。我說一些,指的是如果我得編寫一個測試用例,它進行成千上萬次迭代操作,這樣我才會發現它們在速度上的 1~2% 差異。但是從技術上來說,的確,它的速度要慢一些。重申一下,如果你追求的是行讀取速度 (raw speed) 的話,你就不應該在 Objective-C 中尋找解決方案。

另一個問題是它對新手來說很不友好。這里面有太多的模塊在進行工作了,內部也有很多事情發生,然而這些工作任務就沒有用代碼來明確指明。一個新手可能會覺得:“我在這個 NSOperation 上的私有上下文中保存了這個數據,然后 UI 就發生了更新。這是怎么發生的?”在代碼中并沒有直接的鏈接,因此對于不理解 Core Data 的開發者來說這的確有些難度。然而,公平而言,Core Data 確實對不理解它的人不友好。Core Data 與其他語言中的持久化相比,走上了一條完全不同的道路,因此它十分難懂。

這同樣也會導致代碼量的加重。我必須要談一談人們實在太依賴于代碼塊了。它們將代碼塊當作一個有趣的新東西使用。我曾經見過一個類中有超過 12000 行代碼的持久化存儲層,因為所有事情都在一個閉包中進行。他們可能被這個方式誤導了,因為代碼塊的易用性,很容易導致我們寫出越來越多的代碼。你在代碼塊中嵌入一個又一個的代碼塊,然后六個月后當你重新檢視代碼的時候,你會發現:“為什么這個持久化存儲層全部集中在一個對象里面?”因此,這會導致這個問題的發生。

要解決在這個系統中容易出現的我稱之為“代碼堆疊 (code puke)”的這個問題,比如說,代碼塊中嵌套代碼塊。代碼塊非常好用,但是由于在其中添加另一個代碼塊實在太容易了,因此總會導致左邊縮進越來越長、越來越長。我們不可能讓左邊縮進長出好幾頁,對吧?這是其中一個問題。

除此之外,就沒有什么太大的問題了。在速度上略有下降就導致很多人放棄使用它,這讓我感到十分困惑。

指南(27:20)

關鍵是保證數據源唯一

因此,絕大多數線程和 Core Data 中出現的問題,都可以通過保持用唯一一個 MOC 與用戶界面關聯而直接解決。如果你忽略我的其他建議,只嚴格執行此建議的話,你仍然可以避免人們在運行持久化系統時出現的絕大多數問題。

不要重用子 MOC

如果你在使用“最佳”方式的話,千萬不要重用那些在核心 MOC 下的子 MOC。它們非常廉價,用一次然后丟掉就可以了。不要為它們建立緩存,也不要讓它們和線程建立關聯,因此不要在線程池中重用它們,也不要做一些其他我曾見過的“自作聰明”的事。創建出來,使用,保存,然后就可以扔掉了。它們完完全全是一次性的。

這樣做的原因是因為數據變化只會向高層走,而不會向下流動,也不會平級移動。如果我在核心 MOC 中的一個子 MOC 接受數據的話,我一旦保存了數據,它所造成的變化就會立即在核心 MOC 中得以顯現,然而這個變化卻不會反饋給其他子 MOC 。如果我在內存線程池中包含了 10 個子 MOC以便重用,它們會很快的變得完全不同步。可以將它們視為某一個時間點的快照。不要期望其他變化能夠在它們上面顯現出來。

在子 MOC 存儲中使用 NSFetchedResultsController 會阻塞主線程

如果你在使用 Core Data,你們的 UI 開發過來告訴你“你的 NSFetchedResultsController 阻塞了我的用戶界面。Core Data 就是個垃圾。”的時候,然后他們會拿出 Instrument,然后證明 NSFetchedResultsController 是導致 UI 卡頓的罪魁禍首。出現這個情況的時候,你需要將那個小箭頭放到 NSFetchedResultsController 下方,這樣你就可以發現 實際上 是他們的某個表視圖單元格造成了這個性能問題。

這看起來很盲目。這通常是當人們第一次尋找 UI 性能問題時所做的,簡單而言是因為這是 iPad/iPhone UI 和 Core Data 之間的分歧所在。當你在 Core Data 中進行大量的變動時,這會導致整個用戶界面發生變化,因此問題會在那里顯示出來。

Instruments,Instruments,Instruments,重要的事情說三遍

當我們在使用 Core Data 以及編寫 UI 的時候,我們應當經常使用 Instruments 這個工具。我們應該確定造成性能問題的原因所在,而不是靠日志輸出或者其他測試來猜想原因。在使用 Core Data 、編寫數據輸入和數據輸出的時候應當經常使用 Instruments,并且還要確保你沒有在主線程上執行這些操作。我在寫代碼的時候有無數次覺得“哇,這段代碼好贊”,然而一把它在 Instruments 中運行后,我就發現我在主線程上傳遞了一個 JSON,從而阻塞了 UI。Instruments 會阻止我們做類似的事。它會幫助我們查看數據是如何傳遞的,并且數據傳遞所在的線程,從而避免絕大多數性能問題的發生。

問與答(41:24)

問:一個有 12000 行代碼的類……這些人一年能做幾次代碼校對啊?

Marcus:他們從來沒做過。他們是一個在 San Francisco 剛成立 80 個小時的創業公司。沒有人有時間做代碼校對。這對于小的創業公司來說是一個非常常見的問題。這就是所謂的“創業者心態”:“我們有錢,但我們有不現實的截止時間,所以我們可以讓程序員加班加點以確保能在規定時間內讓產品上線,之后我們再來決定應用應該怎么做比較好。”這種心態驅使著他們。不幸的是,這已經司空見慣了。

問:所以說最后他們開始第二個版本的構建時,決定開啟代碼校對工作,結果卻……

Marcus:沒錯,產品發布之后,他們就把我請去了三個星期,因為他們的主程序去休假了三個星期,然而他們在兩周內就上架了。之后,主程決定去別處工作。我們搭建了一個全新的開發團隊,決心重構整個應用。

問:您說用 Core Data “最佳”方式來組織代碼。我想,當您存儲子 MOC 的時候,應不應該將存儲事件 (save event) 傳送給核心上下文呢,這樣讓子 MOC 最終保存到持久化存儲區域內?或者說,只需要存儲對核心上下文的變更,然后等待某些其他的事件將子 MOC 存儲到數據庫中呢?

Marcus:我想給你一個我最喜歡的答案:這取決于你的需求。當我們使用“最佳”方式設計的時候,我們不再試圖考慮何時存儲這個問題。在舊有方式中,我們曾經會這么考慮:“好吧,我需要在每出現 10 條記錄或者對 UI 變化的時候進行存儲”。如果我們這樣做的話,就可能會得到某種奇怪的記錄,于是我們使用 #define 去定義:“好吧,如果我們每 6 條記錄進行存儲的話,那么就應該足夠快了,這樣我們就不會覺得表視圖會有卡頓的現象了”。然而,使用“最佳”方式設計的話,我們不再受限于此。我們不在主線程上執行 IO 操作,因此我們可以根據我們的需求來決定如何處理這個問題。這完全取決于那時的數據。這個數據是可恢復的嗎?再次獲取這個數據容不容易?如果是,我可能會過會兒才進行存儲,也可能會在退出的時候進行存儲,還有可能完全不管這事兒,因為就像 推ter 廣播一樣我們可以重新獲取。此外,這個記錄要不要用在其他地方呢?如果是,我就會打算立即對其進行存儲,從而讓其成為一個通用數據或者通用的業務需求。這個數據是不是很有價值?恢復這個數據容不容易?如果恢復很困難,甚至不可恢復的話,我們或許會對其進行保存,進行備份,創建拷貝。然而,如果獲取十分容易的話,我或許會在之后對其保存,可能會在主線程將其拋出,也可能在退出應用或者用戶觀看視頻的時候進行保存。萬一我們在啟動 推ter 的時候接到了電話呢?檢測到這個情形后就可以對數據進行存儲了。這讓我們可以根據需求來決定是否“我必須立即對數據進行保存,因為這會影響到 UI”這個問題。

問:如果你必須要有兩個子上下文的話,那么如何在它們之間共享變化呢?

Marcus:這個操作是完全開放的,你可以實現它們,但是這和平常的操作格格不入。您可以使用通知來強制其中一個子上下文從其他子上下文那里接收更新,但是不要這么做。這是一個非常糟糕的注意。最好的辦法就是將這種方法通通拋棄。如果你必須要要有兩個子上下文的話,并且其中一個將會決定另一個的行為表現的話,那么應該確定第一個之后才創建第二個。創建子上下文非常簡單,因此你應該在需要的時候才創建它們。這樣做的話,或許看起來就像同一個操作了,這樣你就可以用相同的上下文來操作不同的部分。不過不要試圖在子上下文之間共享數據,這會導致意想不到的情況發生。

問:我知道您對 Realm 非常熟悉。我想知道您對 Realm 以及 Core Data 的看法。我們應該使用哪一個呢,或者說哪一個更加適合您呢?

Marcus:我會說一些關于 Realm 的問題。首先,我對第三方代碼的觀念已經眾所周知了:所有的這些代碼都糟糕透頂。我覺得 Realm 在試圖解決一個本身就不正確的問題。他們試圖比 Core Data 更快,而 Core Data 已經足夠快了,只是可維護性較差。我在嘗試 Realm 的過程中,發現兩者的代碼寫起來基本差不多。他們的遷移操作對我來說也有點詭異。他們試圖更快,這是一個好主意,不過這并不是我想要的。作為一名項目經理或者說一名開發者,我所要的是可維護性和連貫性。我對第三方框架的首要看法就是他們如同曇花一現,沒有什么可持續性。這事發生了一遍又一遍。我們不知道 Realm 能夠存在多久,我不是很了解他們的商業模型。Core Data 對我來說也已經足夠了。它已經非常成熟了,存在了很久,并且也已經足夠快了。如果它的速度不夠快的話,就說明我可能在哪做錯了。關于 Realm 來說我仍然還有很多未知的地方。它的存儲機制沒有公開,這讓我有點害怕。相反,對于 Core Data 而言,它已經是廣為人知了。蘋果不會拋棄它的。SQLite 的機制是透明的,我可以查看數據,我可以獲取數據。即使第二天它就垮了,我仍然可以查看它們。對我來說,這已經足夠好了,這也是我經常所使用的工具。對于 Realm 來說有沒有什么不好的呢?并沒有,大家可以盡情地嘗試。它或許對大家來說十分有用,不過對我來說,它并沒有解決我想要的問題。它并沒有顯著優于 Core Data,你沒法說:“哇,這簡直太贊了,為什么還會有人用 Core Data呢?”,相反,你只能說:“好吧,這非常快。棒極了,挺好的。”它的代碼仍然很多,并且它還沒有 Core Data 那樣成熟。一年后再來問我這個問題,或許我就會改變我的想法了。

Realm 注:我們認為我們的首要設計目標是簡化開發和增強可維護性,而不是增加速度。特別是,我們已經花費了很多時間來設計線程模型,我們相信這應該是比 Core Data 更為簡單的。 Realm 的 Objective-C 和 Swift 框架 層 目前已經開源,我們的底層存儲層也即將開源。關于我們商業模型的更多信息,您可以隨時查看我們的價格頁面,或者如果有任何問題話歡迎來聯系我們!

</div>

See the discussion on Hacker News .

Sign up to be notified of new videos — we won’t email you for any other reason, ever.

</div> </div>

來自: https://realm.io/cn/news/marcus-zarra-core-data-threading/

</span></span>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!