国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

ReactiveCocoa代碼實踐之-RAC網絡請求重構

2019-12-12 06:33:10
字體:
來源:轉載
供稿:網友

相關閱讀:

ReactiveCocoa代碼實踐之-UI組件的RAC信號操作

ReactiveCocoa代碼實踐之-更多思考

前言

•RAC相比以往的開發模式主要有以下優點:提供了統一的消息傳遞機制;提供了多種奇妙且高效的信號操作方法;配合MVVM設計模式和RAC宏綁定減少多端依賴。

•RAC的理論知識非常深厚,包含有FRP,高階函數,冷信號與熱信號,RAC Operation,信號的生命周期等,這些文檔里都有介紹。 但是由于RAC本身的特性,可能會聽上去容易上手難。

•本文還是從一個比較接地氣的角度開始的。因為現在要做一個完美100%的全項目ReactiveCocoa架構基本不太現實,大多數項目都會有很多歷史包袱,我們只能漸漸的向RAC靠攏,將一段段惡心的代碼重構,使邏輯功能更加清晰。

本節主要我之前對網絡請求的重構的一個簡單記錄。

一.普通請求重構

舊代碼結構圖:


之前的代碼控制器中都是一個個需要連接網絡的方法中直接調用service的請求方法并獲取回調,屬于常規做法。

// controller.m ************************************// 控制器中的某一處方法- (void)requestForTop{[MDSBezelActivityView activityViewForView:self.view withLabel:@"加載中..."];// 直接調用service里的請求方法[SXFeedbackService requestForFeedbackSummarySuccess:^(NSDictionary *result) {[MDSBezelActivityView removeView];// 成功后相關處理} failure:^(AFHTTPRequestOperation *operation, NSError *error) {[MDSBezelActivityView removeView];// 失敗后相關處理}];}

重構后結構圖:

使用RAC改寫后,controller不會直接調用service,controller通過控制一個個command的執行與否來達到發請求的目的。得到數據后綁定的值一旦發生改變,會來到RACObserve的回調方法。并且如果請求失敗,也會以錯誤信號的方式傳遞到execute的subscribeError回調方法里。 executing可以用來監聽命令是否執行完。

// controller.m ************************************@property(nonatomic,strong)SXFeedbackMainViewModel *viewModel;- (void)viewDidLoad{[self addRACObserve];}// 在頁面初次加載時設置綁定- (void)addRACObserve{@weakify(self);[[RACObserve(self.viewModel, topNumEntity) skip:] subscribeNext:^(id x) {@strongify(self);// 綁定viewModel的值一旦改變來到這里。}];}// 原本用來發請求的地方- (void)requestForTop{[[self.viewModel.fetchFeedbackSummaryCommand execute:nil] subscribeError:^(NSError *error) {// 對錯誤的處理}];[[self.viewModel.fetchFeedbackSummaryCommand.executing skip:] subscribeNext:^(NSNumber *executing) {if ([executing boolValue]) {[MDSBezelActivityView activityViewForView:self.view withLabel:@"加載中..."];}else{[MDSBezelActivityView removeView];}}];}// viewModel.m ************************************- (instancetype)init{self = [super init];[self setupRACCommand];return self;}// 初始化設定一個指令用來打開某個請求- (void) setupRACCommand{@weakify(self);_fetchFeedbackSummaryCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {// 這里面更徹底的方法是直接將請求寫成一個operation,但是大多數項目的網絡層應該都有manager或是簽名等原因想直接改成那種結構可能比較復雜 ,所以這里面的代碼像是RAC和直接請求的結合。[SXMerchantAutorityService requestForFeedbackSummarySuccess:^(NSDictionary *result) {@strongify(self);// 成功回調后做的相關操作[subscriber sendCompleted];} failure:^(AFHTTPRequestOperation *operation, NSError *error) {[subscriber sendError:error];}];return nil;}];}];}

二.需要傳參數的請求

上面是普通的請求,就是請求地址是寫死或者是從全局變量中拼接參數的。 如果需要傳入若干參數的話controller無法直接接觸到service,所以需要以viewModel作為媒介傳值,有兩種傳值方法。

1.通過viewModel的屬性

這種方法可用于參數少,一個或兩個的。直接在viewModel里加上一些屬性,然后controller在適當的時候給這個屬性賦值。 在viewModel中的RACCommand中調用service方法需要參數時直接從自己的屬性取。

// controller.m ************************************self.viewModel.isAccess = self.isAccess;[self requestForTop];// viewModel.h ************************************// input參數/*** 是美團還是點評*/@property(nonatomic, assign) BOOL isAccess;// viewModel.m ************************************_fetchFeedbackSummaryCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {[SXMerchantAutorityService requestForFeedbackSummaryWithType:self.isAccess success:^(NSDictionary *result) {// 成功} failure:^(AFHTTPRequestOperation *operation, NSError *error) {// 失敗}];return nil;}];}];

如果是用RAC宏設置viewModel和controller的某些屬性綁定,那也可以省去手動給viewModel的set方法賦值這一步。(董鉑然博客園)

2.通過execute方法參數傳值

這種方法適用于參數較多的情況無法一一列為viewModel的屬性。 這時候建議設置一個對象模型,然后在execute方法前將這個模型建立好并賦值,然后作為參數傳入。

比如這種常見的列表類的具有多個參數的請求方法:

// service.h ************************************/*** 獲取評價列表*/+ (void)requestForFeedbacklistWithSource:(BOOL)isFromWebdealid:(NSInteger)dealidpoiid:(NSInteger)poiidlabelName:(NSString *)labelNametype:(NSString *)typereadStatus:(NSString *)readStatusreplyStatus:(NSString *)replyStatuslimit:(NSNumber *)limitoffset:(NSNumber *)offsetsuccess:(void(^)(NSDictionary *result))successfailure:(void(^)(AFHTTPRequestOperation *operation, NSError *error))failure; 

在controller的發請求方法中舊方法就是直接調用service的請求接口,這里不再列出,下面列出RAC的寫法。

// controller.m ************************************- (void)requestForDataWithType:(int)type{// ------給RACComand傳入一個input模型。SXFeedbackListRequestModel *input = [SXFeedbackListRequestModel new];input.replyStatus = self.replyStatus; // 這里也可以寫成一個工廠方法input.readStatus = self.readStatus;input.isMeituan = self.isMeituan;input.dealid = self.dealid;input.poiid = self.poiid;input.type = self.type;input.labelName = labelName;input.offset = @(self.offset);input.limit = @();// 上面的input在這里作為參數傳入[[self.viewModel.fetchFeedbackListCommand execute:input] subscribeNext:^(id x) {// ------這里處理正確的操作。} error:^(NSError *error) {// ------這里處理失敗的操作。}];}// viewModel.m ************************************- (void) setupRACCommand{_fetchFeedbackListCommand = [[RACCommand alloc]initWithSignalBlock:^RACSignal *(SXFeedbackListRequestModel *input) {return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {// 用前面execute傳入的參數會傳到這個地方[SXMerchantAutorityService requestForFeedbacklistWithSource:input.isFormWeb dealid:input.dealid poiid:input.poiid labelName:input.labelName type:input.type readStatus:input.readStatus replyStatus:input.replyStatus limit:input.limit offset:input.offset success:^(NSDictionary *result) { @strongify(self);// 一些操作[subscriber sendCompleted];} failure:^(AFHTTPRequestOperation *operation, NSError *error) {[subscriber sendError:error];}];return nil;}];}];}

可能會覺得在這個command中要把之前的模型的每一個屬性都扒出來傳到參數里行為有點冗余。 可以將之前service里的那個參數很多的方法改寫成只需要傳入一個模型。然后command這里就可以直接傳入模型了,反正在方法內部再取出來也不麻煩。我這邊考慮到了其他非RAC地方的兼容性就沒有改了。

三.所有請求完成才消除toast

這里是一個類似于請求combo的概念。所有的請求全部結束后才消除加載中的progressHUD ,如果在普通的架構下可用dispatch調度組來解決,但是RAC實現這個功能非常簡單,主要方法是通過executing信號來判斷一個命令的的狀態,然后使用combineLatest操作來監聽多個command的狀態,combineLatest操作的特征是監聽的多個信號只要有一個改變了就把所有信號組成一個tuple返回。

// 監聽executingRACSignal *hud = [RACSignal combineLatest:@[self.viewModel.fetchFeedbackListCommand.executing,self.viewModel.fetchFeedbackSummaryCommand.executing]];[hud subscribeNext:^(RACTuple *x) {if (![x.first boolValue]&&![x.second boolValue]) {[MDSBezelActivityView removeView];}else{[MDSBezelActivityView activityViewForView:self.view withLabel:@"加載中..."];}}];

這個建議和之前RACObserve寫在一起。 也可以改成filter的寫法。

// 可以把加載HUD的代碼寫在最前面,然后后面直接控制消除HUD[[hud filter:^BOOL(RACTuple *x) {return ![x.first boolValue]&&![x.second boolValue];}] subscribeNext:^(id x) {[MDSBezelActivityView removeView];}]; 

還有另一種方法也可以實現這種需求,rac_liftSelector這個方法是只有所有數組中的信號都發出sendNext信號時才會調用那個@selector的方法,并且這個方法的三個參數分別就是那三個sendNext發的。 所有的都回來了再統一打包,這主要適用于三個請求都是異步沒有依賴關系。

@weakify(self);[[self rac_liftSelector:@selector(doWithA:withB:withC) withSignalsFromArray:@[signalA,signalB,signalC]] subscribeError:^(NSError *error) {@strongify(self);[MDSBezelActivityView removeView];} completed:^{[MDSBezelActivityView removeView];}]; 

combineLatest和liftselector兩種combo的方法有一定的區別,具體的使用可以結合需求。前者是每一個請求回來了都會回調一下,后者是全部回來了再調用方法。(董鉑然博客園)

四.結果數據的傳遞

如果是希望所有的請求都完成了所有數據都獲得了,后再刷新界面,使用上面統一消除toast的方法時同樣適合的。 把消除toast那行代碼改成[self.tableVIew reloadData]或其他代碼即可。

因為現在的主流是希望能夠瘦身Controller, 所以一般也建議將業務邏輯、判斷、計算、拼接字符串放在viewModel里,最后直接把需要的數據返回,控制器只負責得到干脆的數據后直接展示界面。 下面的例子是一個文本標簽上文字的獲得方法

// Controller.m ************************************// ViewDidLoadRAC(self.replyCountLabel,text) = RACObserve(self.viewModel, replyCountLabelTitle);// ViewModel.m ************************************_fetchNewsDetailCommand = [[RACCommand alloc]initWithSignalBlock:^RACSignal *(id input) {return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {@strongify(self);[self requestForNewsDetailSuccess:^(NSDictionary *result) {// 這邊省去一些判空代碼self.detailModel = [SXNewsDetailEntity detailWithDict:result[self.newsModel.docid]];// 中間還有一些其他的操作省略NSInteger count = [self.newsModel.replyCount intValue];// 這里是直接把拼接好的標題返回,現實中還會遇到更復雜的邏輯if ([self.newsModel.replyCount intValue] > ) {self.replyCountBtnTitle = [NSString stringWithFormat:@"%.f萬跟帖",count/.];}else{self.replyCountBtnTitle = [NSString stringWithFormat:@"%ld跟帖",count];}[subscriber sendCompleted];} failure:^(AFHTTPRequestOperation *operation, NSError *error) {[subscriber sendError:error];}];return nil;}];}]; 

重構時可以將更多控制器的屬性比如模型,或數組,放到viewModel里。 以前控制器里的self.replyModels 改成self.ViewModel.replyModels。

// ViewModel.h ************************************/*** 相似新聞*/@property(nonatomic,strong)NSArray *similarNews;/*** 搜索關鍵字*/@property(nonatomic,strong)NSArray *keywordSearch;/*** 獲取搜索結果數組命令*/@property(nonatomic, strong) RACCommand *fetchNewsDetailCommand;// ViewModel.m ************************************// 某個command里調用發請求方法成功的回調內self.similarNews = [SXSimilarNewsEntity objectArrayWithKeyValuesArray:result[self.newsModel.docid][@"relative_sys"]];self.keywordSearch = result[self.newsModel.docid][@"keyword_search"];[subscriber sendCompleted];// Controller.m ************************************// 隨便拿了個方法舉例- (CGFloat )tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{switch (section) {case :return self.webView.height;break;case :return self.viewModel.replyModels.count > ? : CGFLOAT_MIN;break;case :return self.viewModel.similarNews.count > ? : CGFLOAT_MIN;break;default:return CGFLOAT_MIN;break;}}

合理的分離之后應該是Controller只有一些UI控件,ViewModel中存放模型屬性,命令,和一些業務邏輯操作或判斷的方法等。

對其中的一些demo代碼感興趣的可以fork下這里的代碼 https://github.com/dsxNiubility/SXNews 。以前是用土方法寫了個小項目,現在舊代碼移到了old分支,master分支上持續在做一些RAC相關的改動。

以上所述是小編給大家介紹的ReactiveCocoa代碼實踐之-RAC網絡請求重構 的相關內容,希望對大家有所幫助!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 万宁市| 巫溪县| 仲巴县| 武威市| 祁阳县| 庆阳市| 西乌珠穆沁旗| 凤庆县| 连城县| 盘山县| 新乡县| 平原县| 舞阳县| 辰溪县| 平顺县| 彰武县| 娄烦县| 汶川县| 武宣县| 于都县| 东辽县| 祥云县| 杭州市| 宁国市| 盈江县| 嘉善县| 芦溪县| 鄯善县| 额济纳旗| 峨山| 青海省| 宜城市| 浏阳市| 灵宝市| 天气| 贵南县| 昌黎县| 广西| 青川县| 虎林市| 信宜市|