假設有一個類微博列表,有一個時間label,顯示相似剛剛、1天前這種。後端返回時間戳,對應Model字段pubTime.格式化後的結果,姑且叫pubTimeFormat.pubTime到fomrat轉換邏輯的存放位置,決定了你的架構模式。程序員
針對上文中的第3點,有人可能會反駁,我寫在cell內,跟VC有什麼關係? 假設這個列表須要按天像日記同樣分組顯示,你會怎麼作?在VC中遍歷數組,判斷時間,而後分組。這二者的本質實際上是同樣的,只不是一個是cell.contentView這種子view,一種是self.view這種父view,也就是開頭說的,轉換邏輯的存放位置,決定了你的架構模式.後端
在iOS中,view和viewControoler的界限並無那麼涇渭分明,每一個VC自帶一個容器view.再好比tableView的數據源方法中,咱們常常寫的cell.goodModel = self.models[indexPath.row]這種代碼。從MVC的設計上來講,view是不該該知道model的細節的,但咱們每每會在cell中引入Model頭文件,進行各類配置。數組
若是必定要遵照view和Model隔離的原則,咱們可能會在VC中寫出緩存
cell.userName = model.userName;
cell.publishTime = publishTimeFormat
複製代碼
這種代碼,view和model徹底的隔離。但想必你會寧肯選擇以前的寫法。bash
爲解決這個疑問,讓咱們回到設計初衷來,爲何要隔離view和model呢?或者說不隔離會有什麼問題?網絡
model的定義應該是數據層,包括數據模型和相關數據的增刪改查這類操做。Controller應該能夠從model層直接獲取數據,而後作數據的分發操做。架構
iOS開發中,model的概念很容易被簡化爲數據模型,網絡請求->字典轉模型->控制器持有數據源數組->模型賦值給cell,咱們已經習慣了這種流程。app
而若是Model不承擔起數據層應有的職責,相關的代碼天然被咱們寫到了Controller中,致使controller的臃腫。框架
而若是像開頭說的,爲知足不一樣頁面的UI需求,咱們可能會不斷往Model中添加各類屬性,也就是業界所說的胖Model.隨之帶來的問題就是model隨着業務增加愈來愈爆炸,愈來愈難移植複用。學習
解答以前的疑問,隔離view和model,本質是要隔離view和數據層的增刪改查操做。若是view能接觸到數據層操做的細節,那麼view就是一個全能的模塊,它既對外接收用戶的交互,又能自行操做數據層完成交互對應的數據更新。那這個view的複雜度可想而知.
ViewModel是基於胖model的架構思路創建的,而後在胖model中拆分出Model和ViewModel.
關於MVVM,廣泛說法是爲Controller瘦身.其實準確來講,是胖model隨着業務增加後帶來了拆分的需求。而那種把轉換代碼寫在Controller中,而後從Controoler中挪到ViewModel的,雖然是異曲同工,但這種演化路徑顯然是不科學的。
不少初次接觸VM的,每每不知道VM裏究竟該放哪些元素。其實viewAdapter可能更好的表達了viewModel的意圖。它經過某些方式(網絡請求、本地緩存)獲取原始數據,根據視圖的展現邏輯加工數據,並對外提供加工後的數據。
viewModel不是替代Controller的,他只承擔展現邏輯,不承擔視圖操做邏輯。viewModel原則上不該該引入UIKit頭文件,更不處理push、present等跳轉邏輯。
iOS中可用的綁定機制沒什麼選擇,面對陡峭的學習曲線,惟一的疑問就是要不要。個人答案是要。
若是說VM是給M或C減負,那麼綁定機制實際上是給程序員減負。
想象一下假若有一個相似網易雲音樂的界面,有各類業務邏輯,取列表的第一首歌的封面作列表封面,有一個Label顯示總歌曲數,有一個label滾動顯示當前的歌曲等等等等。假如如今,咱們側滑刪除一首歌。咱們得手動觸發相關視圖的更新邏輯,調用各個模塊的update方法,甚至有些update方法前後調用都必須遵照必定的順序。這就考驗咱們程序員,在寫代碼的時候必須腦海中時刻牢記各類業務邏輯,防止漏更新。而一個後接手的程序員,該怎麼下手。
引入綁定機制意味着什麼,至關於咱們單獨開一個區域,全部的業務邏輯都在這裏了,寫完這個方法後,咱們就能夠釋放腦海中業務相關的堆棧了。假如一個新程序員接手處理刪除這個邏輯,他只須要關心操做數據自己,也就是相似[self.dataArr removeObjectAtIndex]。數據變化後,業務邏輯相關的視圖就能隨數據源的改變自動更新到位。
在項目中,咱們常常能見到baseViewController、baseView、baseModel等等這些基礎類。關於這個話題,咱們只須要問本身兩個問題:1.是否有須要?2. 是否有更好的實現方式?
是否有須要,答案毫無疑問是yes.以baseViewController爲例,裏面每每會配置全局的背景顏色、隱藏顯示系統導航欄樣式、側滑返回、各個頁面生命週期的鉤子等等。這些都是必要的配置,並且須要全局統一遵照。
回答第二個問題前,讓咱們先看一下繼承存在什麼弊端。假若有兩個app,A和B.B中有咱們本身寫的模塊,好比搜索模塊。如今A也須要這個模塊。假如是使用繼承寫的,咱們面對的第一個問題就是拔出蘿蔔帶出泥。BApp中的控制器都繼承了BBaseViewController,也就是說和BApp這個運行環境高度耦合。第二個問題就是如何接入AApp的環境,A也有個ABaseViewController配置了必要的環境信息。
選擇了繼承就意味着選擇了主動耦合環境,此時就要慎重考量這個環境。好比如下兩種狀況耦合環境是沒有問題的。1.繼承cocoa touch框架中的類;2.獨立模塊內繼承模塊內的類;這種狀況下,環境是無感的。
理想的狀況應該是:業務不須要經過繼承即可以對ViewController進行統一配置。業務即便脫離環境,也可以獨立運行;ViewController一旦放入環境,不須要添加額外的代碼即能自動獲取環境賦予的配置和能力。具體實現上,就是環境須要有主動攔截能力,攔截到後注入環境配置代碼,也就是AOP。
以AOP代替baseViewController爲例.首先咱們須要一個UIViewController+nonBase的分類,包括各類本來寫在基類裏的方法和屬性(關聯對象).
第二步,咱們須要替換相似[super method]的效果,讓本來定義在基類裏的配置代碼能夠默認生效。天然咱們容易想到method swizzing + Category的方案。新建第二個類ViewControllerIntercepter攔截器,在load方法裏對UIViewController的相關方法進行攔截,並執行須要默認生效的相關配置。
第三步:僅從功能的角度,前兩步其實已經達到了咱們要求。但在項目實踐中,咱們可能並不想這麼完全的侵入。好比FDFullScreenPopGestrue這個框架,就是用相似的思路實現了無感的全屏側滑返回。但某些頁面,可能不想要或不適合側滑返回。咱們但願具體的頁面能夠選擇接入環境或拒絕接入。雖然這樣會致使一些額外的代碼,但兼顧了遷移時的風險可控性。具體實現時,咱們能夠定義viewControllerProtocal,攔截後判斷被攔截對象有無遵照這個協議來判斷是否執行環境配置。