Realm的常規使用與線程中的坑


結識 Realm 的催化劑

       在我們公司的項目迭代中,由於在之前的聊天這個模塊關於用戶信息的傳值有問題,而之前因為項目經過很多開發者的手,且不提整體的架構有多混亂,就單說緩存這塊,就是亂的不行,有的地方用CoreData,有的地方用FMDB, 而且封裝的Manager中方法的聲明很亂,存取的邏輯也不是很清晰,於是造成了很多我需要取到數據的時候,根本取不到,而當我修改大部分本次版本迭代的需求時,發現這個取不到值問題如果繼續沿用之前的邏輯就會非常的麻煩,我要用很多額外方法去跳過之前的坑,只是我決定,在群組聊天的功能中,將用戶數據的傳值這塊邏輯重構.     

        傳值的邏輯還是比較好重構的,我將原有用在這里的 CoreData代碼全部都清掉,讓整體功能即使不依賴於緩存,依舊可以正確及時的取到需要的數據,只是需要等待服務器的響應,但是如果每次都去走服務端的網絡請求,那么體驗就太差了,那么緩存便是很重要的一步.

        之前說過,現有項目有,有的地方采用了 CoreData,有的用了 FMDB,十分混亂,而我們的用戶量還並涉及不到數據遷移的問題.所以我想采用另一套緩存框架來完成我的需求,那么我第一個想到的就是 Realm.

初識 Realm

        Realm是一個跨平台的移動數據庫引擎,而且,它是專門為移動端數據應用設計的數據持久化方案.不論是 CoreData,還是傳統的SQLite,代碼都些許冗余.CoreData的笨拙的API和FMDB相對不那么面向對象的操作方式,可能會很多人望而卻步或萌生停用的念頭,那么這個時候,Realm 出現了.

      Realm既不是 CoreData,也不是SQLite,它擁有自己的數據庫存取引擎,它可以跨平台使用,也意味着更加快速的存取速度,官方給出的Realm的存取速度比 CoreData快了3倍,但是據說在實際使用中,當數據量很大時候,Realm的速度比 CoreData 快了不值30倍.

      說了這么多,你一定對 Realm 也有了些許好感,這篇文章中我並打算介紹關於 Realm 的使用方法,因為文檔Relam的官方文檔中寫的清清楚楚,網上很多大神也做了相關介紹,但是大多數的博客中方法的介紹都是局限在了 Demo 的使用中,真的作用於項目的很少,我在此也算賣弄賣弄我在植入到實際項目時所遇到的小坑或者經驗吧.

  1.由於 RLMArray 的關系,這句話一定要寫,來定義 RLMArray 中的實例,不然會崩潰


 

   2.由於數據模型已經由繼承與 NSObject 的 Model, 改為了繼承於 RLMObject,所以在使用 KVC 的時候一定要注意.

3.主鍵

如果你想要更新數據,主鍵是不錯的選擇

4.線程

       線程問題的坑是我這篇文章所要說的重點.其實 Realm 在關於線程的處理上已經幫我們做了很多事情,我自己並不需要講過多的精力放在線程上,但是 Realm 本身的線程管理非常嚴格,所以我們必須遵守Realm 的使用方式,這就使得坑與有點並存

        原本我最開始關於緩存的設計思路是,我從服務端拿到了數據,那么我會把數據放入內存中的數組容器中,再存入數據庫中,那么下次進入這個頁面我就可以先從數據庫中拿到數據后放入容器,再通過服務端進行更新,也是說在當前 Controller 中,我只需要通過數組容器進行賦值就可以了.然而 Realm 並不允許你這樣,當我將數據存入 Realm 后,我還沒有進行取數據的操作,我只是用數據容器,但這時,程序崩潰了, WTF????!!!! 查看一下崩潰信息 Realm accessed from incorrect thread(從錯誤的線程訪問),當時我就委屈了,我存的時候沒有崩潰,也沒取啊,怎么就崩潰的,后來我又不得已的用蹩腳的英語水平仔細研讀了一下原文文檔.

蛋疼的開始


同一個 Realm提交過寫入就可以在其他線程被各種蹂躪

Realm的實例不是安全線程的不能在其他線程或列隊被訪問

卧槽???看的我是萬臉懵逼!!

       於是我就交了個 Realm的技術交流群,開始,還有人跟我交流交流使用心德,等把把圖貼出來,石沉大海一般,可能是兩張截圖暴露我的智商和理解能力,大家不想跟差生玩兒...好吧,本着 API 文檔就像游戲攻略一樣的原則,看不懂的,就帶着疑問去玩一玩...那么既然增沒事兒,改刪查也都沒做,那問題會不會出現在了 RLMObject 的調用上,於是在遍歷使用之前說的數組容器的地方,打印了當前線程,嗯,果然不是主線程,那既然說同一個 Realm 只要提交了寫入就可以在其他線程改變,那我於是試了試 [RLMObject  objectWhere:@"查詢條件"],發現就完全沒有問題的,那原因到底是什么呢?於是我又開始啃文檔


你可以有任意數量的線程並行工作在同一個Realm

只是唯一要注意的是你不能在多個線程共享同一個 RealmObject實例

        這就非常清晰了, Realm 本身在工程中的調用也是個單例類[RLMRealm defaultRealm],所以只要是同一個 Realm, 就可以在任意線程,哪怕是多個線程中,隨意使用,不需要鎖,只需要將 commitWriteTransaction就可以.但是 RLMObject 的使用的限制就非常嚴格的,主線程里創建的 RLMObject 就只能在主線程里用,在其他線程中調用的這個它的實例就會拋出異常,有人說,這是 Realm的線程坑,但是我覺得這個是 Realm 對線程做的最好的處理

為此我特意寫了一段非常欠打的代碼,來驗證 Realm 是如何處理並發問題的

、、、

#pragma mark 這是第一個子線程  這里面進行更新寫入

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

NSLog(@"current ======= %@",[NSThread currentThread]);

for (int i = 0; i < 35; i ++) {

InformationUpdateModel *model = [[InformationUpdateModel alloc] init];

model.name = [NSString stringWithFormat:@"草鞋%d號",i];

model.age = i;

RLMRealm *relam = [RLMRealm defaultRealm];

[relam transactionWithBlock:^{

[relam addOrUpdateObject:model];

[relam commitWriteTransaction];

}];

}

dispatch_async(dispatch_get_main_queue(), ^{

[InfomationModel allObjects];

});

});

、、、

#pragma mark 這是第二個子線程  這里面是查詢

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

NSLog(@"current ======= %@",[NSThread currentThread]);

[InfomationModel allObjects];

});

#pragma mark 這里是第三個子線程  這里是更新寫入加查詢  回到主線程后繼續更新寫入加查詢

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

NSLog(@"current ======= %@",[NSThread currentThread]);

for (int i = 60; i < 80; i ++) {

InformationUpdateModel *model = [[InformationUpdateModel alloc] init];

model.name = [NSString stringWithFormat:@"草鞋%d號",i];

model.age = i;

RLMRealm *relam = [RLMRealm defaultRealm];

[relam transactionWithBlock:^{

[relam addOrUpdateObject:model];

[relam commitWriteTransaction];

}];

}

[InfomationModel allObjects];

dispatch_async(dispatch_get_main_queue(), ^{

for (int i = 40; i < 50; i ++) {

InformationUpdateModel *model = [[InformationUpdateModel alloc] init];

model.name = [NSString stringWithFormat:@"草鞋%d號",i];

model.age = i;

RLMRealm *relam = [RLMRealm defaultRealm];

[relam transactionWithBlock:^{

[relam addOrUpdateObject:model];

[relam commitWriteTransaction];

}];

}

});

});

我直接開了三個子線程,並在其中用同一個 Realm 各自存取


三個子線程

從時間上來看,形成了一個小規模的並發,也是說,會在Realm可能同時即被讀,又被寫,但是完全沒有報出異常,程序流暢運行,數據也沒有出現錯誤.


由於MVCC 架構,不會阻塞讀寫

關於 Realm 就先說到這里,其實 Realm 也有很多不便之處,比如我們的模型必須要繼承RLMObject,他的RLMArray也並不是 NSArray類型,所以從代碼的獨立性角度上來說,就不是特別的完美,如果本身項目中有一套完整嚴格的數據協議,那么 Realm 可能就不會是一個好的選擇,而且本身自帶 Model,也被很多架構師的去 Model 化思想想違背,但這並不妨礙它是一套專門用於移動端,速度效率超快, API 非常簡潔,線程處理非常棒的持久化解決方案.

 

后記

        第一次寫技術文章,誠惶誠恐,其實從代碼角度來說,並沒有分享什么優質代碼,而且廢話比較多,可能是因為我個人嘴太碎.這篇文章只是針對自己對於 Realm 的使用做了一些總結,並希望分享出去,這樣如果我有理解的不對或者值得討論的地方,也可以盡快的糾正,當然如果這邊文章可以幫到誰,哪怕只有那么一丟丟,我也就心滿意足了

        其實關於 Realm 的線程處理還有很多更好的方法,如果有機會和時間我會隨着業務的深入,再次進行探索,並將心得分享出來共勉.

       如果有問題或者不對地方我會及時更正.



作者:Wilson魏
鏈接:http://www.jianshu.com/p/dd5c7f926218
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM