iOS客戶端中的不可變模型與數(shù)據(jù)一致性

翻譯:https://engineering.pinterest.com/blog/immutable-models-and-data-consistency-our-ios-app

今年早些時候,為了讓iOS客戶端反應(yīng)更快,體驗更簡明逼友,特別是針對非美國用戶夺鲜,我們對iOS客戶端進(jìn)行了重構(gòu)变勇。一個重構(gòu)的目標(biāo)就是憨攒,將客戶端的模型層變?yōu)橥耆豢勺儭_@篇文章中待笑,我將討論這種做法背后的動機,探討我們的新系統(tǒng)在更新模型抓谴,加載網(wǎng)絡(luò)接口數(shù)據(jù)和保持?jǐn)?shù)據(jù)一致性方面的做法暮蹂。

不可變模型?

“不可變模型”是近期經(jīng)常聽到討論的術(shù)語癌压,而且很多客戶端已經(jīng)轉(zhuǎn)為不可變模型仰泻。不可變意味著模型一旦初始化完成之后就不能再修改。為什么要使用呢滩届,可變模型的主要問題是數(shù)據(jù)處于共享狀態(tài)集侯。

考慮下面這種情況,在一個可變模型系統(tǒng)中帜消,A和B都引用了C棠枉。

Image1.png

如果A修改了C,A和B都將得到變化后的值泡挺。這很好辈讶,但是如果B不期望如此,就會發(fā)生問題娄猫。

舉個例子贱除,在消息界面中生闲,有兩個其他的用戶。每個消息對象都有一個“用戶”屬性勘伺。

Image2.png

當(dāng)我停留在這個頁面上時跪腹,客戶端中的其它部分把Devin從對話中移除(可能是接收了服務(wù)器的回應(yīng),更改了數(shù)據(jù)模型)飞醉。這時我點擊了第二行想屏蔽Devin冲茸,會關(guān)聯(lián)用戶列表中的第二個對象。將要返回的是Stephanie而不是Devin缅帘,最終我把錯誤的人加入了黑名單轴术。

不可變對象是線程安全的。以前钦无,我們得擔(dān)心一個線程寫數(shù)據(jù)的時候逗栽,另一個線程正在讀數(shù)據(jù)。在新系統(tǒng)中失暂,對象自從創(chuàng)建后就不可以更改彼宠,所以我們可以安全的并發(fā)的使用多線程讀取數(shù)據(jù),而不用擔(dān)心數(shù)據(jù)錯亂弟塞。這讓開發(fā)變得更輕松凭峡,因為客戶端可以越來越支持并發(fā)和多線程。

更新數(shù)據(jù)模型

自從數(shù)據(jù)模型創(chuàng)建后就變得完全不可變决记,唯一更新或者說改變的方法是構(gòu)造一個新的模型摧冀。有兩種方式來做這件事:

  1. 使用字典來初始化模型(通常來自json報文)

     User *user = [[User alloc] initWithDictionary:dictionary];
    
  2. 使用構(gòu)造器對象,通常是擁有數(shù)據(jù)模型所有屬性的可變描述系宫∷靼海可以基于存在的數(shù)據(jù)模型中創(chuàng)建一個構(gòu)造器,修改你想修改的屬性扩借,然后調(diào)用initWithBuilder方法來返回一個全新模型椒惨。(關(guān)于這一點后續(xù)文章會有更多介紹)

     // Change the current user's username to “taylorswift”
     UserBuilder *userBuilder = [[UserBuilder alloc] initWithModel:self.currentUser];
     userBuilder.username = @"taylorswift";
     self.currentUser = [[User alloc] initWithBuilder:userBuilder];
    

加載和緩存接口數(shù)據(jù)

我們的接口允許從服務(wù)器拉取部分模型數(shù)據(jù),模型屬性的部分子集往枷。比如在圖釘列表頁框产,只需要圖片鏈接和描述即可,并不需要全部數(shù)據(jù)错洁,直到用戶進(jìn)入圖釘聚合頁才需要食譜原料(recipe ingredients)屬性秉宿。

Image3.png

客戶端中有一個PINCache數(shù)據(jù)緩存中心,PINCache是一個數(shù)據(jù)模型緩存庫屯碴,已經(jīng)開源描睦。緩存的key是服務(wù)器為每個模型提供的唯一的ID。每當(dāng)我們從服務(wù)器得到數(shù)據(jù)导而,先從緩存中心里檢查模型是否存在忱叭。如果存在隔崎,將服務(wù)器的新的數(shù)據(jù)和模型存在的屬性進(jìn)行融合,生成新的模型韵丑。新的模型將替代緩存中舊的模型爵卒。這樣,緩存的模型總是擁有我們接收到的所有最新屬性撵彻。

Image4.png

數(shù)據(jù)一致性

模型改變(比如新建一個模型)钓株,應(yīng)該通知到展示模型的視圖。我們之前使用KVO來完成陌僵,但是KVO對不可變不起作用轴合,他只觀察一個實例的改變。現(xiàn)在我們使用基于NSNotificationCenter的通知系統(tǒng)來廣播數(shù)據(jù)模型的更改碗短。

觀察變化

視圖或者視圖控制器可以注冊成為模型更新的觀察者受葛。這個例子中,消息頁面控制器監(jiān)聽了他的消息對象的更新通知偎谁∽芴玻控制器需要知道模型變化,因為新的模型可能會更新屬性巡雨。

Image5.png

下面的代碼咳秉,創(chuàng)建了一個觀察者用來監(jiān)聽模型的變化,觀察的名字是模型的唯一標(biāo)識符鸯隅。在這個方法下,使用基于block的NSNotificationCenter接口向挖,這樣可以比較方便的管理觀察著的生命周期蝌以。

[self.notificationManager addObserverForUpdatedModel:self.message block:^(NSNotification *notification) {
// Update message view here!
}];

notificationManager只是一個強引用觀察者的NSObject,由于它是視圖控制器的屬性何之,得確备控制器銷毀的時候,它也會銷毀溶推,并且能夠解除對所有觀察者的引用關(guān)系徊件。

廣播變化

當(dāng)消息對象更新,會廣播一個更新通知

Message *newMessage = [[Message alloc] initWithBuilder:newBuilder];
[NotificationManager postModelUpdatedNotificationWithObject:newMessage];

postModelUpdatedNotificationWithObject方法會查找緩存中心中擁有相同標(biāo)識符的對象蒜危,并將這個對象廣播出去虱痕。

更新界面

收到通知后,新的對象可以通過NSNotification的object屬性獲得辐赞。視圖控制器可以用新的模型對象做任何事情部翘。

__weak __typeof__(self) weakSelf = self;
[self.notificationManager addObserverForUpdatedModel:self.message block:^(NSNotification *notification) {
    __typeof__(self) strongSelf = weakSelf;
    Message *newMessage = (Message *)notification.object;
    strongSelf.usersInMessageThread = newMessage.users;
    [strongSelf.tableView reloadData];
}];

待更新

對于我們這種體量的客戶端來說,完全替換為不可變模型不是件易事响委,在這個過程中新思,我們也創(chuàng)造了一些很棒的工具來輔助開發(fā)窖梁。下一片文章,將介紹我們是如何進(jìn)行自動合成模型類以及其它知識夹囚。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纵刘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子荸哟,更是在濱河造成了極大的恐慌假哎,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敲茄,死亡現(xiàn)場離奇詭異位谋,居然都是意外死亡,警方通過查閱死者的電腦和手機堰燎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門掏父,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人秆剪,你說我怎么就攤上這事赊淑。” “怎么了仅讽?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵陶缺,是天一觀的道長。 經(jīng)常有香客問我洁灵,道長饱岸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任徽千,我火速辦了婚禮苫费,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘双抽。我一直安慰自己百框,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布牍汹。 她就那樣靜靜地躺著铐维,像睡著了一般。 火紅的嫁衣襯著肌膚如雪慎菲。 梳的紋絲不亂的頭發(fā)上嫁蛇,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音露该,去河邊找鬼棠众。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的闸拿。 我是一名探鬼主播空盼,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼新荤!你這毒婦竟也來了揽趾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤苛骨,失蹤者是張志新(化名)和其女友劉穎篱瞎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痒芝,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡俐筋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了严衬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片澄者。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖请琳,靈堂內(nèi)的尸體忽然破棺而出粱挡,到底是詐尸還是另有隱情,我是刑警寧澤俄精,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布询筏,位于F島的核電站,受9級特大地震影響竖慧,放射性物質(zhì)發(fā)生泄漏嫌套。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一圾旨、第九天 我趴在偏房一處隱蔽的房頂上張望灌危。 院中可真熱鬧,春花似錦碳胳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至产雹,卻和暖如春诫惭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蔓挖。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工夕土, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓怨绣,卻偏偏與公主長得像角溃,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子篮撑,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容