欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

ReactiveCocoa代碼實(shí)踐之-RAC網(wǎng)絡(luò)請(qǐng)求重構(gòu)

 更新時(shí)間:2016年04月20日 10:49:18   作者:董鉑然  
這篇文章主要介紹了ReactiveCocoa代碼實(shí)踐之-RAC網(wǎng)絡(luò)請(qǐng)求重構(gòu) 的相關(guān)資料,需要的朋友可以參考下

相關(guān)閱讀:

ReactiveCocoa代碼實(shí)踐之-UI組件的RAC信號(hào)操作

ReactiveCocoa代碼實(shí)踐之-更多思考

前言

•RAC相比以往的開發(fā)模式主要有以下優(yōu)點(diǎn):提供了統(tǒng)一的消息傳遞機(jī)制;提供了多種奇妙且高效的信號(hào)操作方法;配合MVVM設(shè)計(jì)模式和RAC宏綁定減少多端依賴。

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

•本文還是從一個(gè)比較接地氣的角度開始的。因?yàn)楝F(xiàn)在要做一個(gè)完美100%的全項(xiàng)目ReactiveCocoa架構(gòu)基本不太現(xiàn)實(shí),大多數(shù)項(xiàng)目都會(huì)有很多歷史包袱,我們只能漸漸的向RAC靠攏,將一段段惡心的代碼重構(gòu),使邏輯功能更加清晰。

本節(jié)主要我之前對(duì)網(wǎng)絡(luò)請(qǐng)求的重構(gòu)的一個(gè)簡(jiǎn)單記錄。

一.普通請(qǐng)求重構(gòu)

舊代碼結(jié)構(gòu)圖:


之前的代碼控制器中都是一個(gè)個(gè)需要連接網(wǎng)絡(luò)的方法中直接調(diào)用service的請(qǐng)求方法并獲取回調(diào),屬于常規(guī)做法。

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

重構(gòu)后結(jié)構(gòu)圖:

使用RAC改寫后,controller不會(huì)直接調(diào)用service,controller通過控制一個(gè)個(gè)command的執(zhí)行與否來達(dá)到發(fā)請(qǐng)求的目的。得到數(shù)據(jù)后綁定的值一旦發(fā)生改變,會(huì)來到RACObserve的回調(diào)方法。并且如果請(qǐng)求失敗,也會(huì)以錯(cuò)誤信號(hào)的方式傳遞到execute的subscribeError回調(diào)方法里。 executing可以用來監(jiān)聽命令是否執(zhí)行完。

// controller.m ************************************
@property(nonatomic,strong)SXFeedbackMainViewModel *viewModel;
- (void)viewDidLoad{
[self addRACObserve];
}
// 在頁(yè)面初次加載時(shí)設(shè)置綁定
- (void)addRACObserve{
@weakify(self);
[[RACObserve(self.viewModel, topNumEntity) skip:] subscribeNext:^(id x) {
@strongify(self);
// 綁定viewModel的值一旦改變來到這里。
}];
}
// 原本用來發(fā)請(qǐng)求的地方
- (void)requestForTop
{
[[self.viewModel.fetchFeedbackSummaryCommand execute:nil] subscribeError:^(NSError *error) {
// 對(duì)錯(cuò)誤的處理
}];
[[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;
}
// 初始化設(shè)定一個(gè)指令用來打開某個(gè)請(qǐng)求
- (void) setupRACCommand
{
@weakify(self);
_fetchFeedbackSummaryCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 這里面更徹底的方法是直接將請(qǐng)求寫成一個(gè)operation,但是大多數(shù)項(xiàng)目的網(wǎng)絡(luò)層應(yīng)該都有manager或是簽名等原因想直接改成那種結(jié)構(gòu)可能比較復(fù)雜 ,所以這里面的代碼像是RAC和直接請(qǐng)求的結(jié)合。
[SXMerchantAutorityService requestForFeedbackSummarySuccess:^(NSDictionary *result) {
@strongify(self);
// 成功回調(diào)后做的相關(guān)操作
[subscriber sendCompleted];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[subscriber sendError:error];
}];
return nil;
}];
}];
}

二.需要傳參數(shù)的請(qǐng)求

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

1.通過viewModel的屬性

這種方法可用于參數(shù)少,一個(gè)或兩個(gè)的。直接在viewModel里加上一些屬性,然后controller在適當(dāng)?shù)臅r(shí)候給這個(gè)屬性賦值。 在viewModel中的RACCommand中調(diào)用service方法需要參數(shù)時(shí)直接從自己的屬性取。

// controller.m ************************************
self.viewModel.isAccess = self.isAccess;
[self requestForTop];
// viewModel.h ************************************
// input參數(shù)
/**
* 是美團(tuán)還是點(diǎn)評(píng)
*/
@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宏設(shè)置viewModel和controller的某些屬性綁定,那也可以省去手動(dòng)給viewModel的set方法賦值這一步。(董鉑然博客園)

2.通過execute方法參數(shù)傳值

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

比如這種常見的列表類的具有多個(gè)參數(shù)的請(qǐng)求方法:

// service.h ************************************
/**
* 獲取評(píng)價(jià)列表
*/
+ (void)requestForFeedbacklistWithSource:(BOOL)isFromWeb
dealid:(NSInteger)dealid
poiid:(NSInteger)poiid
labelName:(NSString *)labelName
type:(NSString *)type
readStatus:(NSString *)readStatus
replyStatus:(NSString *)replyStatus
limit:(NSNumber *)limit
offset:(NSNumber *)offset
success:(void(^)(NSDictionary *result))success
failure:(void(^)(AFHTTPRequestOperation *operation, NSError *error))failure; 

在controller的發(fā)請(qǐng)求方法中舊方法就是直接調(diào)用service的請(qǐng)求接口,這里不再列出,下面列出RAC的寫法。

// controller.m ************************************
- (void)requestForDataWithType:(int)type
{
// ------給RACComand傳入一個(gè)input模型。
SXFeedbackListRequestModel *input = [SXFeedbackListRequestModel new];
input.replyStatus = self.replyStatus; // 這里也可以寫成一個(gè)工廠方法
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在這里作為參數(shù)傳入
[[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傳入的參數(shù)會(huì)傳到這個(gè)地方
[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;
}];
}];
}

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

三.所有請(qǐng)求完成才消除toast

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

// 監(jiān)聽executing
RACSignal *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:@"加載中..."];
}
}];

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

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

還有另一種方法也可以實(shí)現(xiàn)這種需求,rac_liftSelector這個(gè)方法是只有所有數(shù)組中的信號(hào)都發(fā)出sendNext信號(hào)時(shí)才會(huì)調(diào)用那個(gè)@selector的方法,并且這個(gè)方法的三個(gè)參數(shù)分別就是那三個(gè)sendNext發(fā)的。 所有的都回來了再統(tǒng)一打包,這主要適用于三個(gè)請(qǐng)求都是異步?jīng)]有依賴關(guān)系。

@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的方法有一定的區(qū)別,具體的使用可以結(jié)合需求。前者是每一個(gè)請(qǐng)求回來了都會(huì)回調(diào)一下,后者是全部回來了再調(diào)用方法。(董鉑然博客園)

四.結(jié)果數(shù)據(jù)的傳遞

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

因?yàn)楝F(xiàn)在的主流是希望能夠瘦身Controller, 所以一般也建議將業(yè)務(wù)邏輯、判斷、計(jì)算、拼接字符串放在viewModel里,最后直接把需要的數(shù)據(jù)返回,控制器只負(fù)責(zé)得到干脆的數(shù)據(jù)后直接展示界面。 下面的例子是一個(gè)文本標(biāo)簽上文字的獲得方法

// Controller.m ************************************
// ViewDidLoad
RAC(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];
// 這里是直接把拼接好的標(biāo)題返回,現(xiàn)實(shí)中還會(huì)遇到更復(fù)雜的邏輯
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;
}];
}]; 

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

// ViewModel.h ************************************
/**
* 相似新聞
*/
@property(nonatomic,strong)NSArray *similarNews;
/**
* 搜索關(guān)鍵字
*/
@property(nonatomic,strong)NSArray *keywordSearch;
/**
* 獲取搜索結(jié)果數(shù)組命令
*/
@property(nonatomic, strong) RACCommand *fetchNewsDetailCommand;
// ViewModel.m ************************************
// 某個(gè)command里調(diào)用發(fā)請(qǐng)求方法成功的回調(diào)內(nèi)
self.similarNews = [SXSimilarNewsEntity objectArrayWithKeyValuesArray:result[self.newsModel.docid][@"relative_sys"]];
self.keywordSearch = result[self.newsModel.docid][@"keyword_search"];
[subscriber sendCompleted];
// Controller.m ************************************
// 隨便拿了個(gè)方法舉例
- (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;
}
}

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

對(duì)其中的一些demo代碼感興趣的可以fork下這里的代碼 https://github.com/dsxNiubility/SXNews 。以前是用土方法寫了個(gè)小項(xiàng)目,現(xiàn)在舊代碼移到了old分支,master分支上持續(xù)在做一些RAC相關(guān)的改動(dòng)。

以上所述是小編給大家介紹的ReactiveCocoa代碼實(shí)踐之-RAC網(wǎng)絡(luò)請(qǐng)求重構(gòu) 的相關(guān)內(nèi)容,希望對(duì)大家有所幫助!

相關(guān)文章

最新評(píng)論