電商項目(MVVM+ReactiveCocoa)

MVVM架構(gòu)

自然是按照ViewModel、ViewController桂敛、View奈附、Model的順序依次構(gòu)建。

RACObserve循環(huán)引用

self.rac_willDeallocSignal信號—>block—->self—->rac_willDeallocSignal信號

MVVM構(gòu)造更加輕量化ViewController
  • 控制器里面所有屬于事件處理的邏輯和計算通通搬到ViewModel里面去说订,換句話說,控制器就干兩件事情潮瓶,第一、響應(yīng)用戶的交互钙姊;第二毯辅,呈現(xiàn)給用戶交互結(jié)果。

  • 問題的復(fù)雜性就在于此煞额,典型的一個購物車思恐,你選中一個商品,這是一個交互膊毁,而交互的結(jié)果卻是多個的胀莹,包括總價格要變化,按鈕的使能要變化婚温,商品的總數(shù)要發(fā)生變化描焰,交給ViewModel來處理,好了你至少要計算出三個交互結(jié)果栅螟,然后三次使用Block或是委托來實現(xiàn)逆向傳值荆秦,才能把結(jié)果ViewModel的處理結(jié)果傳遞到控制器去,如果不是三個而是5個6個力图,那很有可能帶來的結(jié)果是步绸,本來是想精簡控制器的代碼所以設(shè)置ViewModel,最后因為事件處理的結(jié)果和多吃媒,使用了一大堆的Block和委托來傳值瓤介,反而把控制器給弄亂了吕喘,因此很多開發(fā)者如果不會使用RAC卻使用MVVM的框架是很考驗?zāi)托牡模€不如直接把事件處理寫在控制器來的直接呢刑桑,雖然控制器冗余了點氯质,但是還談不上亂呀。

  • 如果使用RAC來處理控制器和ViewModel之間的協(xié)作漾月,所有的問題迎刃而解病梢。原因就是用戶交互終究只會觸發(fā)一個信號,無論最后這個用戶交互事件需要呈現(xiàn)多少個連鎖反應(yīng)梁肿,所有與這個操作有關(guān)的UI元素訂閱這個信號不就行了么蜓陌,一個信號對應(yīng)多個連鎖反應(yīng),就是這么神奇吩蔑。

  • 那么問題來了钮热,都有哪些方法把控制器的事件傳遞到ViewModel里面去呢 ?現(xiàn)在最常用到的就是第一:RACObserve觀察者主要傳遞控制器的輸入內(nèi)容變化烛芬;第二:RACCommand主要用于耗時事件隧期;第三:RACSubject用于立即響應(yīng)按鈕事件,例如簡單地跳轉(zhuǎn)個控制器啥的赘娄。

ViewModel和ViewController的交互
  • ViewModel處理網(wǎng)絡(luò)請求仆潮,網(wǎng)絡(luò)請求的最終結(jié)果通過ViewModelRACComand信號傳遞到控制器。

  • 控制器持有ViewModel對象遣臼,意味著控制器持有ViewModel對象的所有屬性性置,ViewModel對象的屬性可是實時通過RACObserver記錄控制器的值,對ViewModel的屬性做更新監(jiān)聽揍堰,一旦屬性變化一次鹏浅,就釋放一次信號,進(jìn)行判斷后給控制器一個反饋信號屏歹。

  • 完整的邏輯鏈條就是輸入框輸入內(nèi)容隐砸,釋放以內(nèi)容為值的信號,ViewModel的屬性綁定了這個信號蝙眶,將輸入框值自動綁定了自己的屬性上季希、ViewModel的另一個信號屬性又訂閱了這個屬性的值的變化,一旦變化械馆,則發(fā)送一個信號出去胖眷,然后控制器的輸入框和登錄按鈕的使能狀態(tài)又訂閱了這個信號、再然后就是一個完整的響應(yīng)鏈條霹崎,實現(xiàn)控制器最初發(fā)送信號珊搀,ViewModel處理信號,然后再發(fā)出信號尾菇,控制器再響應(yīng)信號的閉環(huán)生態(tài)境析。

  • 簡單地說囚枪,控制器發(fā)A信號同時訂閱ViewModel處理完A信號發(fā)送的B信號。

  • 如果需要及時反饋劳淆,那么直接發(fā)送RACSingle信號链沼,如果對于A信號的處理是一個異步耗時操作,那么RACCommand命令內(nèi)嵌RACSingle的形式返回B信號沛鸵。RACComand里面的信號執(zhí)行異步耗時操作括勺,返回成功或失敗,成功就發(fā)送值為字典的信號曲掰,失敗就發(fā)送錯誤信號疾捍。

BaseViewModel
  • BaseViewModel的屬性包括title、jumpTool栏妖、paramsDict乱豆。初始化BaseViewModel時就需要傳入paramsDict,意義在于self.title = paramsDict[@"title"]給控制器的導(dǎo)航欄標(biāo)題賦值吊趾,除此之外宛裕,paramsDict還可以傳遞其它的參數(shù),當(dāng)然是以字典鍵值對的形式進(jìn)行傳遞论泛。jumpTool作為一個繼承于NSObject的對象揩尸,持有navigationController屬性值,封裝一系列控制器跳轉(zhuǎn)的方法屁奏,意義在于處理navigationController或是待跳轉(zhuǎn)viewController不存在時的異常疲酌。

  • baseViewModel作為初始化baseViewController的形參,意味著初始化baseViewcontroller之前必須先實例化一個baseViewModel了袁,因為baseViewModel里面就持有baseViewController所需要的所有數(shù)據(jù),包括最重要的導(dǎo)航欄標(biāo)題湿颅,這就是相對于屬性正向傳值的優(yōu)點载绿,可以在初始化viewController的時候就把數(shù)據(jù)賦值給控制器。當(dāng)然為了全局的使用和對傳進(jìn)來的viewModel進(jìn)一步操作油航,就需要將初始化控制器傳進(jìn)來的viewModel賦值給self.baseViewModel崭庸。

  • MVC都是在viewController創(chuàng)建控制器,然后self.navigationController跳轉(zhuǎn)控制器谊囚。為了在viewModel里面跳轉(zhuǎn)控制器怕享,這里則是通過ViewModel.jumpTool來調(diào)用方法跳轉(zhuǎn)控制器。 一個tabarItem對應(yīng)著一個navigationController镰踏,創(chuàng)建navigationController導(dǎo)航控制器的時候函筋,需要初始化一個繼承于BaseViewController的普通控制器作為根控制器,在BaseViewControllerviewDidLoad方法里將navigationController存在baseViewModel.jumpTool.navigation屬性里奠伪。如此一來跌帐,JumpTool控制器跳轉(zhuǎn)幫助類就可以在持有navigationController的基礎(chǔ)上隨意封裝任何形參的控制器跳轉(zhuǎn)方法首懈。只要誰持有了控制器跳轉(zhuǎn)幫助類對象,誰就可以在任意地方執(zhí)行控制器跳轉(zhuǎn)谨敛。

  • 繼承于BaseViewModel基類的viewmodel都會重寫基類的init方法究履,意義是子類ViewModel相比父類BaseViewModel擁有更多的屬性需要初始化,而且必須是在初始化ViewModel對象的同時就來實例子類里面的這些新增屬性脸狸。擴(kuò)展這些屬性都挺簡單的的最仑,能想到的唯一稍微麻煩的地方就是初始化VeiwModel的新增RACComand屬性了。

  • UI控件持有ViewModel目的是利用viewModel響應(yīng)控制器的用戶交互事件或者說是為了將觸發(fā)事件傳遞到viewModel里炊甲,比如按鈕點擊命令啥的泥彤。

BaseViewcontroller
  • 首頁控制器是通過StoryBoard初始化的,意味著沒有按照BaseViewController基類里面的初始化方法進(jìn)行初始化蜜葱。添加了右滑退出控制器的手勢全景,這對于控制器的退出是十分重要且必要的。

  • BaseViewController父類里的初始化方法寫了重要的一步牵囤,傳入navigationController參數(shù)初始化BaseViewModel屬性爸黄。自然ViewMode屬性值為空,自然在調(diào)用父類viewDidLoad方法給BaseViewModel.jumpTool屬性賦值無法實現(xiàn)揭鳞,因此難以跳轉(zhuǎn)炕贵。

  • BaseViewController設(shè)置ViewModel屬性,因為每個子類控制器都會設(shè)置這個ViewModel屬性野崇,干脆在基類里面設(shè)置ViewModel屬性称开,帶來的問題就是子類的ViewModel屬性本質(zhì)上與BaseViewController里面的viewModel屬性是子類父類的關(guān)系,意味著子類控制器去調(diào)用BaseViewControllerViewModle屬性的方法乓梨,會造成水土不服鳖轰。

  • 解決方法就是在繼承于BaseViewController的子類控制器里面聲明@dynamic viewModel,如此一來扶镀,ViewModel所屬Class不再是BaseViewModel蕴侣,而是屬于子類Class。這樣來看臭觉,并沒有起到父類幫子類聲明統(tǒng)一屬性的方法昆雀,如果子類用的屬性跟父類用的屬性聲明的方法相同,自然沒問題蝠筑,可是子類的屬性與父類的屬性是子類父類的關(guān)系狞膘,這就需要子類改寫父類的成員變量的類型,變量名稱不變什乙,Xcode提示你父類和子類聲明了重復(fù)的成員變量挽封,使用@dynamic告訴Xcode成員變量所屬Class的關(guān)系就可以了。

  • BaseViewController本來不該寫這個屬性的稳强,但是不得已而為之场仲,十分需要BaseViewModel屬性來保存實例控制器時傳進(jìn)來的ViewModel和悦,必須依賴baseViewController.baseViewModel.jumpTool保存navigationController才能實現(xiàn)控制器的跳轉(zhuǎn)。否則渠缕,每一個繼承于BaseViewController基類控制器的子類控制器都需要重寫一些基類的init方法鸽素,不然根本就沒法保存初始化控制器時傳進(jìn)來的viewModel。其實后來的通過子類Class再次聲明viewModel和@dynamic本質(zhì)就是為了擴(kuò)展基類BaseViewControllerviewModel屬性的方法和屬性亦鳞。

  • 由類方法初始化控制器變成了由類名字符串[[NSClassFromString(ClassName) alloc]來初始化控制器馍忽。封裝控制器跳轉(zhuǎn)邏輯的唯一原因就是統(tǒng)一規(guī)避所有可能會遇到的控制器跳轉(zhuǎn)異常,異常主要有兩點:一是導(dǎo)航控制器不存在燕差,二是待跳轉(zhuǎn)的控制器構(gòu)建不出來遭笋。
商品Cell
  • Cell下方存在加入購物車的邏輯,扯出了購物車管理器徒探、購物車管理器又扯出了用戶管理器瓦呼、用戶管理器有扯出了地址管理器、現(xiàn)在迷失在地址管理器测暗,難以自拔央串!
RACComand
  • UIButton擴(kuò)展了rac_command的屬性,將buttonenable狀態(tài)與command命令的執(zhí)行來狀態(tài)綁定碗啄,用戶點擊按鈕時质和,command命令會自動執(zhí)行,同時按鈕enable置為NO稚字。如果手動執(zhí)行command命令饲宿,則可以發(fā)送參數(shù)。

  • 使用RACComand命令的時候的傳入的參數(shù)胆描,這個參數(shù)到底是什么地方傳進(jìn)來的瘫想,有什么用,就像屬性傳值是的昌讲,只要有RACComand這個對象殿托,就意味著可以把對象傳遞到RACComand里面去。誰調(diào)用excue這個消息剧蚣,誰就有資格傳遞信息進(jìn)行正向傳值。

  • 使用RACComand的過程中旋廷,如果只是創(chuàng)建了一個信號鸠按,那么直接返回這個信號就可以了,同時這個信號在異步操作執(zhí)行完成之后發(fā)送執(zhí)行結(jié)果給那些訂閱此信號的人饶碘。信號在把消息發(fā)送出去的同時目尖,也銷毀了信號本身,一旦RACComand檢測到內(nèi)部持有的信號已經(jīng)銷毀扎运,必然改變自己的執(zhí)行狀態(tài)瑟曲,表示信號里面的耗時任務(wù)執(zhí)行完畢饮戳。于是RACComand的狀態(tài)改成了執(zhí)行完畢。那么現(xiàn)在是一個RACConmand里面三個信號洞拨,信號3是一個刷新UI的信號扯罐,依賴信號1和信號2去請求數(shù)據(jù),信號1和信號2同時成功后再去觸發(fā)信號3烦衣。

  • RACCommand的初始化必須實現(xiàn)Block參數(shù)代碼塊歹河,而且這個Block參數(shù)代碼塊很特別,既有輸入值id類型數(shù)據(jù)花吟,又有輸出值RACSignal秸歧,輸入值來自于ViewModel操作RACCommand屬性執(zhí)行Excute命令時,可以帶一個參數(shù)過來衅澈,這個參數(shù)可以是任何形式键菱,用來區(qū)分這個命令操作到底是屬于誰。返回值必須是一個信號今布,就算你什么都不做经备,也必須在Block參數(shù)代碼塊里面返回一個[RACSignal empty]空信號,這就是一個典型的有輸入值有返回值的Block函數(shù)形參了险耀。

  • 如果是要在RACCommand事件里面進(jìn)行一個異步操作弄喘,就不能返回空信號了,不能返回空信號甩牺,必須返回一個冷信號蘑志,冷信號作用就是先把信號創(chuàng)建出來,暫且不發(fā)送任何的內(nèi)容贬派,直接在創(chuàng)建信號后的Block形參代碼塊里面寫入將要執(zhí)行的異步操作急但,然后根據(jù)異步操作的執(zhí)行結(jié)果通過訂閱者subscriber發(fā)送不同的信號值,當(dāng)然我們創(chuàng)建的冷信號RACsignal是需要以RACCommandBlock形參返回值返回出去的搞乏。冷信號RACsignalBlock形參的返回值則是一個RACDisposable對象波桩,唯一的意義就是在訂閱者subscriber發(fā)布錯誤信號error或是結(jié)束信號complete之后會銷毀我們創(chuàng)建的RACsignal冷信號,銷毀這個信號的同時會進(jìn)入RACDisposableBlock參數(shù)代碼塊里面请敦。如果需要在冷信號被銷毀之后執(zhí)行某些代碼镐躲,那么RACDisposable顯得特別暖心,平時直接return一下RACDisposable的實例就可以了侍筛。

  • 接下來就是邏輯處理異步請求的結(jié)果萤皂,方式一直接在異步耗時操作的Block回調(diào)進(jìn)行處理;方式二是訂閱這個RACSignal冷信號匣椰,然后根據(jù)不同的信號值裆熙,做出處理。

  • 登錄注冊首先實例RACCommand,后面的Block代碼塊里面直接就return創(chuàng)建RACSignal冷信號入录。RACSignalBlock代碼塊里蛤奥,就是異步網(wǎng)絡(luò)請求,同時類方法Block回調(diào)網(wǎng)絡(luò)請求結(jié)果僚稿,請求成功則subscriber發(fā)送YES凡桥,反之失敗則subscriber發(fā)送NO

  • 創(chuàng)建RACsignal冷信號帶有一個Block代碼塊贫奠。RACDisposable作為與異步網(wǎng)絡(luò)請求并行的關(guān)系必須作為Block代碼塊的返回值進(jìn)行return唬血。

  • 訂閱RACComandBlock代碼塊里面創(chuàng)建的RACSignal。方式一subscriber訂閱RACCommand.executionSignals.switchToLatest信號唤崭;方式二直接在Blcok代碼塊里subscriber訂閱前面創(chuàng)建的RACSignal信號拷恨。

  • 獲取RACCommand執(zhí)行狀態(tài)。方式一subscriber訂閱[RACCommand.executing skip:1]信號來判斷RACCommand執(zhí)行狀態(tài)谢肾。方式二直接在創(chuàng)建RACCommand時默認(rèn)RACCommand執(zhí)行狀態(tài)開始腕侄,在訂閱到RACSignal發(fā)出值信號或RACSignal被銷毀時默認(rèn)執(zhí)行狀態(tài)結(jié)束。

  • subscriber發(fā)送的值信號格式芦疏。如果網(wǎng)絡(luò)請求數(shù)據(jù)成功冕杠,就發(fā)送@{@"code":@100,@"data":responseObject}這個字典,如果請求數(shù)據(jù)失敗酸茴,就發(fā)送@{@"code":@400,@"data":@"請求失敗"}

RACSubject
  • 通過RACSubject信號把View子視圖上的按鈕事件傳遞到ViewModel中區(qū)分预,同時傳遞的參數(shù)主要按鈕的標(biāo)識符Tag號。類似于給View子視圖添加按鈕事件Block回調(diào)薪捍。

  • TableView子視圖持有ViewModel笼痹,在Cell點擊回調(diào)方法里將Cell點擊事件轉(zhuǎn)變成RACSubject信號發(fā)送到ViewModel

購物車數(shù)據(jù)本地化
  • 讀取酪穿。初始化購物車的ViewModel時訂閱全局ShoppingManager單例類change屬性值RACObserve([ShoppingManager manager]凳干,change)信號,獲取[ShoppingManager manager]goodsDic數(shù)據(jù)更新UI被济。

  • 保存救赐。單例類的意義在于實時讀寫數(shù)據(jù)。以商品模型id為鍵只磷、商品模型model為值存入單例的[ShoppingManager manager].goodsDic屬性中经磅。

  • 添加。當(dāng)用戶在GoodManagerView視圖上點擊增加或減少商品數(shù)量的時候钮追,重寫[ShoppingManager manager].goodsDic馋贤。

  • 更新。首先array數(shù)組保存[ShoppingManager manager].goodsDic字典的所有值allValue畏陕,一個值代表一個商品模型model,字典所有模型轉(zhuǎn)移到數(shù)組之后清空goodsDic仿滔;遍歷商品模型數(shù)組array的每一個商品模型model惠毁,通過model.isSelected判斷商品是否被選中犹芹;未選中狀態(tài)則繼續(xù)以商品模型id為鍵、模型model為值存入[ShoppingManager currentUser].goodsDic字典中鞠绰;處于選中狀態(tài)則首先獲取被選中商品模型model的單個商品數(shù)量腰埂,更新用戶管理單例類的[UserManager currentUser].bageValue商品總數(shù)屬性;最后蜈膨,在商品模型數(shù)組array的每一個商品模型model都遍歷一遍之后屿笼,需要將[ShoppingManager manger][UserManager currentUser]這兩個單例類模型對象重新保存到本地沙盒,保證退出應(yīng)用翁巍,購物車數(shù)據(jù)依然存在驴一。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市灶壶,隨后出現(xiàn)的幾起案子肝断,更是在濱河造成了極大的恐慌,老刑警劉巖驰凛,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胸懈,死亡現(xiàn)場離奇詭異,居然都是意外死亡恰响,警方通過查閱死者的電腦和手機(jī)趣钱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胚宦,“玉大人首有,你說我怎么就攤上這事〖浒Γ” “怎么了绞灼?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長呈野。 經(jīng)常有香客問我低矮,道長,這世上最難降的妖魔是什么被冒? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任军掂,我火速辦了婚禮,結(jié)果婚禮上昨悼,老公的妹妹穿的比我還像新娘蝗锥。我一直安慰自己,他們只是感情好率触,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布终议。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪穴张。 梳的紋絲不亂的頭發(fā)上细燎,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機(jī)與錄音皂甘,去河邊找鬼玻驻。 笑死,一個胖子當(dāng)著我的面吹牛偿枕,可吹牛的內(nèi)容都是我干的璧瞬。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼渐夸,長吁一口氣:“原來是場噩夢啊……” “哼嗤锉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起捺萌,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤档冬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后桃纯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酷誓,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年态坦,在試婚紗的時候發(fā)現(xiàn)自己被綠了盐数。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡伞梯,死狀恐怖玫氢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情谜诫,我是刑警寧澤漾峡,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站喻旷,受9級特大地震影響生逸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜且预,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一槽袄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锋谐,春花似錦遍尺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽迂苛。三九已至,卻和暖如春鼓择,著一層夾襖步出監(jiān)牢的瞬間灾部,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工惯退, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人从藤。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓催跪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親夷野。 傳聞我的和親對象是個殘疾皇子懊蒸,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

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