ReactiveCocoa+MVVM

ReactiveCocoa+MVVM

主要介紹ReactiveCocoa的使用(適合入門(mén)選手查看)藐吮,對(duì)于新手來(lái)說(shuō)器虾,先會(huì)用朵锣,然后慢慢推敲原理捞高,理解加靈活運(yùn)用氯材。本文借鑒了這篇不錯(cuò)的文章:最快上手ReactiveCocoa引用了部分注釋和說(shuō)明

前言

說(shuō)起MVC架構(gòu)大家都比較熟悉,也是使用很難廣硝岗,但是MVVM這個(gè)項(xiàng)目架構(gòu)氢哮,搭配函數(shù)響應(yīng)式的編程,會(huì)變得更加如魚(yú)得水型檀,寫(xiě)出的項(xiàng)目也是很具特點(diǎn)冗尤,符合高內(nèi)聚,低耦合的思想胀溺。本博客將主要通過(guò)Github:FanRACExtensions項(xiàng)目來(lái)練習(xí)MVVM框架的ReactiveCocoa響應(yīng)式編程裂七,并且給出一些RAC的擴(kuò)展。

1.ReactiveCocoa+MVVM簡(jiǎn)介

1.1ReactiveCocoa簡(jiǎn)介

ReactiveCocoa(簡(jiǎn)稱(chēng)為RAC),是Github上開(kāi)源的一個(gè)iOS和OS平臺(tái)新框架,該項(xiàng)目是函數(shù)響應(yīng)式編程仓坞,借助大量KVO背零,KVC的綁定,這種設(shè)計(jì)思路代碼可觀性強(qiáng)无埃,目前GitHub上有很多這種成熟框架捉兴。

1.2什么是MVVM?

MVVM想對(duì)應(yīng)于MVC澎埠,實(shí)質(zhì)上沒(méi)有本質(zhì)區(qū)別虽缕,但是MVVM比MVC架構(gòu)中多了一個(gè)ViewModel,就是這個(gè)ViewModel蒲稳,卻是MVVM相對(duì)于MVC改進(jìn)的核心思想氮趋。在開(kāi)發(fā)過(guò)程中伍派,由于需求的變更或添加,項(xiàng)目的復(fù)雜度越來(lái)越高剩胁,代碼量越來(lái)越大诉植,此時(shí)我們會(huì)發(fā)現(xiàn)MVC維護(hù)起來(lái)有些吃力,所以有人想到把Controller的數(shù)據(jù)和邏輯處理部分從中抽離出來(lái)昵观,用一個(gè)專(zhuān)門(mén)的對(duì)象去管理晾腔,這個(gè)對(duì)象就是ViewModel,是Model和Controller之間的一座橋梁啊犬。這樣Controller中的代碼變得非常少灼擂,變得易于測(cè)試和維護(hù),只需要Controller和ViewModel做數(shù)據(jù)綁定即可觉至,可是在實(shí)際使用過(guò)程中剔应,MVVM寫(xiě)出的代碼量并不比MVC的少,有時(shí)反而還會(huì)多點(diǎn)语御,畢竟多了一個(gè)數(shù)據(jù)綁定過(guò)程峻贮,但邏輯會(huì)清晰很多,對(duì)于多人開(kāi)發(fā)的團(tuán)隊(duì)应闯。

2.ReactiveCocoa使用和說(shuō)明

學(xué)習(xí)ReactiveCocoa之前月洛,首先要搞懂幾個(gè)常用的類(lèi)RACSiganl,RACSubscriber,RACDisposable,RACCommand等下面我們一一介紹

2.1 RACSiganl創(chuàng)建于與使用

RACSignal的核心就是,創(chuàng)建信號(hào)createSignal,發(fā)送信號(hào)sendNext,完成信號(hào)sendCompleted孽锥,和銷(xiāo)毀信號(hào)[RACDisposable disposableWithBlock:^{ }];,訂閱信號(hào)subscribeNext.

//信號(hào)量創(chuàng)建
RACSignal *siganl=[[[[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    sleep(2);
    //sendNext通過(guò)信號(hào)量發(fā)送值嚼黔,傳值(可以是異步)訂閱后能接受該值
    [subscriber sendNext:@{@"userName":"username",@"password":"123456"}];
    //報(bào)告信息執(zhí)行完成,類(lèi)似于回調(diào)完成一樣
    [subscriber sendCompleted];
    
    return [RACDisposable disposableWithBlock:^{
            //block調(diào)用時(shí)刻:當(dāng)信號(hào)發(fā)送完成或者發(fā)送錯(cuò)誤惜辑,就會(huì)自動(dòng)執(zhí)行這個(gè)block,取消訂閱信號(hào)唬涧。
    }];
}]map:^id _Nullable(id  _Nullable value) {
    return value;
}] logError] replayLazily]; 

// 訂閱信號(hào),才會(huì)激活信號(hào),只有發(fā)送了sendNext方法才會(huì)調(diào)用該訂閱方法
[siganl subscribeNext:^(id x) {
    // block調(diào)用時(shí)刻:每當(dāng)有信號(hào)發(fā)出數(shù)據(jù),就會(huì)調(diào)用block.
    NSLog(@"接收到數(shù)據(jù):%@",x);
}];
  • RACSubscriber:表示訂閱者的意思盛撑,用于發(fā)送信號(hào)碎节,這是一個(gè)協(xié)議,不是一個(gè)類(lèi)抵卫,只要遵守這個(gè)協(xié)議狮荔,并且實(shí)現(xiàn)方法才能成為訂閱者。通過(guò)create創(chuàng)建的信號(hào)介粘,都有一個(gè)訂閱者殖氏,幫助他發(fā)送數(shù)據(jù)。

  • RACDisposable:用于取消訂閱或者清理資源姻采,當(dāng)信號(hào)發(fā)送完成或者發(fā)送錯(cuò)誤的時(shí)候雅采,就會(huì)自動(dòng)觸發(fā)它。在使用中如果不想監(jiān)聽(tīng)某個(gè)信號(hào)時(shí),可以通過(guò)它主動(dòng)取消訂閱信號(hào)婚瓜。

  • RACSubject:信號(hào)提供者宝鼓,自己可以充當(dāng)信號(hào),又能發(fā)送信號(hào),可以充當(dāng)代理巴刻。

  • RACTupleRACTupleUnpack類(lèi)似于數(shù)組愚铡,元組,集合胡陪,可以處理信號(hào)返回的數(shù)據(jù)

2.2 RACSiganl其他方法及含義

因?yàn)镽AC封裝的原理是沥寥,一切皆信號(hào),所以信號(hào)的其他方法也可以一并了解下

//map數(shù)據(jù)整合(純數(shù)據(jù))督弓,flattenMap數(shù)據(jù)過(guò)濾(內(nèi)部返回的是信號(hào))
[single map:^id _Nullable(RACTuple * value) {
        //這里可以過(guò)濾數(shù)據(jù)(RACTuple表示返回?cái)?shù)據(jù)元組)
        RACTupleUnpack(id response,id responseObject)=value;
        //本來(lái)兩個(gè)參數(shù)返回营曼,傳給下一層就一個(gè)參數(shù)
        return responseObject;
    }]flattenMap:^__kindof RACSignal * _Nullable(id  _Nullable value) {
        //過(guò)濾錯(cuò)誤數(shù)據(jù)
        if ([value isKindOfClass:[NSError class]]) {
            return [RACSignal error:value];
        }
        return [RACSignal return:value];
    }]

  • map 純數(shù)據(jù)組合,返回?cái)?shù)據(jù)
  • filter 數(shù)據(jù)過(guò)濾乒验,返回@(YES)or @(NO)
  • flattenMap 過(guò)濾和返回?cái)?shù)據(jù)信號(hào)量愚隧,錯(cuò)誤信號(hào)和完成信號(hào)
  • ignore 忽略掉什么樣的數(shù)據(jù)一般配合filter使用
  • skip 一般訂閱消息,會(huì)默認(rèn)執(zhí)行一次初始化的操作锻全,[single skip:1]跳過(guò)第一次
  • merge 合并兩個(gè)信號(hào)到一個(gè)信號(hào)上

3.RACCommand使用和說(shuō)明

學(xué)習(xí)RACCommand之前狂塘,我們通過(guò)我這個(gè)例子簡(jiǎn)單來(lái)說(shuō),就是登錄用戶(hù)名和密碼鳄厌,這里面就用到了ViewModel了荞胡,即數(shù)據(jù)的綁定。首先我要知道幾個(gè)概念或者宏了嚎。

  • @weakify(self);=類(lèi)似于弱引用 泪漂,@strongify(self);=強(qiáng)引用,一般放block塊里面歪泳;
  • RAC(<#TARGET, ...#>)用來(lái)綁定UI的文本或?qū)傩灾档絭iewModel里面對(duì)應(yīng)的屬性上(可以理解成KVC綁定)萝勤;
  • RACObserve(TARGET, KEYPATH)訂閱一個(gè)值信號(hào),可以在viewmodel也可在VC里(可以理解成KVO監(jiān)聽(tīng))呐伞;

3.1 使用步驟:

// 一敌卓、RACCommand使用步驟:
// 1.創(chuàng)建命令 initWithSignalBlock:(RACSignal * (^)(id input))signalBlock
// 2.在signalBlock中,創(chuàng)建RACSignal伶氢,并且作為signalBlock的返回值
// 3.執(zhí)行命令 - (RACSignal *)execute:(id)input

// 二趟径、RACCommand使用注意:
// 1.signalBlock必須要返回一個(gè)信號(hào),不能傳nil.
// 2.如果不想要傳遞信號(hào)癣防,直接創(chuàng)建空的信號(hào)[RACSignal empty];
// 3.RACCommand中信號(hào)如果數(shù)據(jù)傳遞完蜗巧,必須調(diào)用[subscriber sendCompleted],這時(shí)命令才會(huì)執(zhí)行完畢蕾盯,否則永遠(yuǎn)處于執(zhí)行中惧蛹。
// 4.RACCommand需要被強(qiáng)引用,否則接收不到RACCommand中的信號(hào),因此RACCommand中的信號(hào)是延遲發(fā)送的香嗓。

// 三迅腔、RACCommand設(shè)計(jì)思想:內(nèi)部signalBlock為什么要返回一個(gè)信號(hào),這個(gè)信號(hào)有什么用靠娱。
// 1.在RAC開(kāi)發(fā)中沧烈,通常會(huì)把網(wǎng)絡(luò)請(qǐng)求封裝到RACCommand,直接執(zhí)行某個(gè)RACCommand就能發(fā)送請(qǐng)求像云。
// 2.當(dāng)RACCommand內(nèi)部請(qǐng)求到數(shù)據(jù)的時(shí)候锌雀,需要把請(qǐng)求的數(shù)據(jù)傳遞給外界,這時(shí)候就需要通過(guò)signalBlock返回的信號(hào)傳遞了迅诬。

// 四腋逆、如何拿到RACCommand中返回信號(hào)發(fā)出的數(shù)據(jù)。
// 1.RACCommand有個(gè)執(zhí)行信號(hào)源executionSignals侈贷,這個(gè)是signal of signals(信號(hào)的信號(hào)),意思是信號(hào)發(fā)出的數(shù)據(jù)是信號(hào)惩歉,不是普通的類(lèi)型。
// 2.訂閱executionSignals就能拿到RACCommand中返回的信號(hào)俏蛮,然后訂閱signalBlock返回的信號(hào)撑蚌,就能獲取發(fā)出的值。
// 3.還可以訂閱errors信號(hào)搏屑,和executing信號(hào)争涌,來(lái)獲取錯(cuò)誤信息和執(zhí)行狀態(tài)改變

// 五、監(jiān)聽(tīng)當(dāng)前命令是否正在執(zhí)行executing

// 六辣恋、使用場(chǎng)景,監(jiān)聽(tīng)按鈕點(diǎn)擊亮垫,網(wǎng)絡(luò)請(qǐng)求

3.2點(diǎn)擊登錄按鈕VC文件

VC頭屬性

@property (weak, nonatomic) IBOutlet UITextField *userNameTextField;
@property (weak, nonatomic) IBOutlet UITextField *passwordTextField;
@property (weak, nonatomic) IBOutlet UIButton *listButton;
@property (weak, nonatomic) IBOutlet UIButton *loginButton;
@property(nonatomic,strong)FanLoginViewModel *loginViewModel;

VC m文件里面可以簡(jiǎn)化成

{
    self.userNameTextField.text=@"18611723209";
    self.passwordTextField.text=@"123456fan";
    self.loginViewModel=[[FanLoginViewModel alloc]init];
    @weakify(self);
    //輸入框文字改變添加訂閱信息
    //    [self.userNameTextField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
    //        NSLog(@"=====:%@",x);
    //    }];
    
    //把文本信號(hào)綁定到ViewModel里面的userName屬性上
    RAC(self.loginViewModel,userName)=self.userNameTextField.rac_textSignal;
    RAC(self.loginViewModel,password)=self.passwordTextField.rac_textSignal;

    //訂閱username改變(可以直接放在loginViewModel里面解決)
    [[[[RACObserve(self.loginViewModel, userName) ignore:nil]filter:^BOOL(id  _Nullable value) {
        //filter過(guò)濾 ignore忽略掉為""的字符串
        return [value length]>0;//長(zhǎng)度大于0開(kāi)始執(zhí)行
    }] map:^id _Nullable(id  _Nullable value) {
        //這里可以改變?cè)瓟?shù)據(jù)(直接修改輸入框里面的值,返回)
        return value;
    }] subscribeNext:^(id  _Nullable x) {
        //值改變的訂閱 類(lèi)似于rac_textSignal信號(hào)訂閱一樣

    }];
    //按鈕信號(hào)綁定到ViewModel的兩個(gè)信號(hào)上伟骨,便于在ViewModel里面操作
    self.listButton.rac_command=self.loginViewModel.listCommand;
    self.loginButton.rac_command=self.loginViewModel.loginCommand;

    //當(dāng)用戶(hù)名長(zhǎng)度>0時(shí)饮潦,數(shù)據(jù)信號(hào)綁定到按鈕的enable屬性上  merge合并信號(hào)
    //    RAC(self.loginButton,enabled) =[[RACObserve(self.userNameTextField, text)  merge:self.userNameTextField.rac_textSignal ] map:^id(NSString *value) {
    //        return @(value.length>0);
    //    }];
        
    //需要確保loginViewModel初始化,不然就沒(méi)有信號(hào)
    //訂閱方式一:rac_liftSelector會(huì)自動(dòng)線(xiàn)執(zhí)行一次  skip跳過(guò)第一次默認(rèn)的執(zhí)行
    [self rac_liftSelector:@selector(toggleHUD:) withSignals:[RACObserve(self.loginViewModel, executing) skip:1], nil];
    //訂閱方式二:
    [self rac_liftSelector:@selector(showMessage:) withSignals:[[RACObserve(self.loginViewModel, error) ignore:nil] map:^id (id value) {
        return [value localizedDescription];
    }], nil];

    [[RACObserve(self.loginViewModel, modelDic) ignore:nil] subscribeNext:^(id x) {
        @strongify(self);
        [self showMessage:@"登錄成功"];
        //跳轉(zhuǎn)到
        NSLog(@"登錄結(jié)果:%@",x);
        NSDictionary *dic=(NSDictionary *)x;
        NSString *session=dic[@"data"][@"PHPSESSID"];
        [FanAppManager shareManager].phpsession=session;
    }];
    //這個(gè)錯(cuò)誤也可以提到ViewModel里面底靠,簡(jiǎn)化vc代碼
    [[RACObserve(self.loginViewModel, error) ignore:nil] subscribeNext:^(id x) {
    //  @strongify(self);
        NSLog(@"error:%@",x);
    }];

}
-(void)toggleHUD:(NSNumber *)state{
    if ([state boolValue]) {
        [self showMessage:@"正在執(zhí)行"];
    }else{
        [self showMessage:@"執(zhí)行完畢"];
    }
}

-(void)showMessage:(NSString *)msg{
    NSLog(@"msg:%@",msg);
}

3.3 ViewModel實(shí)現(xiàn)

FanLoginViewModel.h

@interface FanLoginViewModel : FanRACViewModel
@property (nonatomic, strong) NSString *userName;
@property (nonatomic, strong) NSString *password;
@property (nonatomic, strong) RACCommand *listCommand;
@property (nonatomic, strong) RACCommand *loginCommand;
/**
 *  錯(cuò)誤
 */
@property (nonatomic, strong) NSError *error;
/**
 *  是否正在執(zhí)行
 */
@property (nonatomic, strong) NSNumber *executing;
//網(wǎng)絡(luò)請(qǐng)求接口的處理后的字典數(shù)據(jù)
@property (nonatomic, strong) NSMutableDictionary *modelDic;
@end

FanLoginViewModel.m


#import "FanLoginViewModel.h"
#import "FanAPIManager.h"

@interface FanLoginViewModel()

@property (nonatomic, strong) FanAPIManager *apiManager;
@end

@implementation FanLoginViewModel

- (instancetype)init{
    if((self = [super init])) {
        //初始化網(wǎng)絡(luò)請(qǐng)求類(lèi)
        self.apiManager=[[FanAPIManager alloc]init];
    }
    return self;
}

-(RACCommand *)loginCommand{
    if (_loginCommand==nil) {
        @weakify(self);
        _loginCommand=[[RACCommand alloc]initWithEnabled:[RACSignal combineLatest:@[RACObserve(self, userName), RACObserve(self, password)] reduce:^id (NSString *userName, NSString *password){
            //校驗(yàn)輸入規(guī)則害晦,同時(shí)會(huì)綁定按鈕的enable屬性(這里可以對(duì)輸入校驗(yàn))
            //這里可以用正則表達(dá)式  
            return @(YES);
        }] signalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
            //2.網(wǎng)絡(luò)請(qǐng)求得到結(jié)果
            ShowHUDIMPMessage(@"加載中");
            @strongify(self);
            return [self.apiManager loginWithUserName:self.userName isEmail:NO countryCode:@"86" password:self.password];
        }] ;
        //concat: 連接信號(hào),第一個(gè)信號(hào)必須發(fā)送完成,第二個(gè)信號(hào)才會(huì)被激活
        // 1.RACCommand有個(gè)執(zhí)行信號(hào)源executionSignals暑中,這個(gè)是signal of signals(信號(hào)的信號(hào)),意思是信號(hào)發(fā)出的數(shù)據(jù)是信號(hào)壹瘟,不是普通的類(lèi)型。
        // 2.訂閱executionSignals就能拿到RACCommand中返回的信號(hào)鳄逾,然后訂閱signalBlock返回的信號(hào)稻轨,就能獲取發(fā)出的值。
        //把網(wǎng)絡(luò)請(qǐng)求的信號(hào)的數(shù)據(jù)合并到這個(gè)輸出里面
        [[_loginCommand.executionSignals concat] subscribeNext:^(id  _Nullable x) {
            //3.訂閱結(jié)果
            @strongify(self);
            self.modelDic = x;
        }];
        // switchToLatest:用于signal of signals雕凹,獲取signal of signals發(fā)出的最新信號(hào),也就是可以直接拿到RACCommand中的信號(hào)
//        [_loginCommand.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {
//
//        }];
        [_loginCommand.errors subscribeNext:^(NSError * _Nullable x) {
            //4.訂閱錯(cuò)誤信息
            @strongify(self);
            self.error = x;
            //下面這個(gè)是我測(cè)試打印的消息殴俱,因?yàn)槟玫降氖嵌M(jìn)制政冻,后臺(tái)的崩潰數(shù)據(jù),不是正常的error
            NSData *errorData=self.error.userInfo[FanRACAFNErrorKey];
            NSString *resultStr=[[NSString alloc]initWithData:errorData encoding:NSUTF8StringEncoding];
            if (resultStr==nil) {
                resultStr=@"data->UTF8 = null";
            }
            NSLog(@"error:%@",resultStr);
        }];
        
        [_loginCommand.executing  subscribeNext:^(NSNumber * _Nullable x) {
            //訂閱執(zhí)行的狀態(tài)
            @strongify(self);
            //如果VC文件里面訂閱了這個(gè)值线欲,就能拿到這個(gè)值的狀態(tài)信息明场,做出不同的UI展示
            self.executing = x;
            if ([self.executing boolValue]) {
                HideHUDAll;
            }
        }];
         
    }
    return _loginCommand;
}

@end

3.4 AFHTTPSessionManager+FanRACExtension 擴(kuò)展實(shí)現(xiàn)

我把AFHTTPSessionManager這個(gè)類(lèi)寫(xiě)了一個(gè)信號(hào)的擴(kuò)展,以前有個(gè)pod庫(kù)李丰,但是很舊了苦锨,仿寫(xiě)了一個(gè)最新的,主要目的就是使用信號(hào)量可以從UI綁定到網(wǎng)絡(luò)請(qǐng)求趴泌,全部弄成RAC框架模式舟舒,當(dāng)然,其他的第三方庫(kù)嗜憔,你也可以仿照寫(xiě)一個(gè)信號(hào)量秃励。

extern NSString *const FanRACAFNErrorKey;

@interface AFHTTPSessionManager (FanRACExtension)
///GET
-(RACSignal*)fan_racGET:(NSString *)URLString parameters:(nullable id)parameters;
///GET-Headers
-(RACSignal*)fan_racGET:(NSString *)URLString parameters:(nullable id)parameters headers:(nullable NSDictionary<NSString *,NSString *> *)headers;

///HEAD
-(RACSignal*)fan_racHEAD:(NSString *)URLString parameters:(nullable id)parameters;
///HEAD-Headers
-(RACSignal*)fan_racHEAD:(NSString *)URLString parameters:(nullable id)parameters headers:(nullable NSDictionary<NSString *,NSString *> *)headers;

///POST
-(RACSignal*)fan_racPOST:(NSString *)URLString parameters:(nullable id)parameters;
///POST-Headers
-(RACSignal*)fan_racPOST:(NSString *)URLString parameters:(nullable id)parameters headers:(nullable NSDictionary<NSString *,NSString *> *)headers;

///PUT
-(RACSignal*)fan_racPUT:(NSString *)URLString parameters:(nullable id)parameters;
///PUT-Headers
-(RACSignal*)fan_racPUT:(NSString *)URLString parameters:(nullable id)parameters headers:(nullable NSDictionary<NSString *,NSString *> *)headers;

///PATH
-(RACSignal*)fan_racPATCH:(NSString *)URLString parameters:(nullable id)parameters;
///PATH-Headers
-(RACSignal*)fan_racPATCH:(NSString *)URLString parameters:(nullable id)parameters headers:(nullable NSDictionary<NSString *,NSString *> *)headers;

///DELETE
-(RACSignal*)fan_racDELETE:(NSString *)URLString parameters:(nullable id)parameters;
///DELETE-Headers
-(RACSignal*)fan_racDELETE:(NSString *)URLString parameters:(nullable id)parameters headers:(nullable NSDictionary<NSString *,NSString *> *)headers;

///GET-POST-PUT等等-通用方法
- (RACSignal *)fan_racWithHTTPMethod:(NSString *)method
                           URLString:(NSString *)URLString
                          parameters:(nullable id)parameters
                             headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                      uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                    downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress;


///POST-Headers-Upload File(上傳文件)
-(RACSignal*)fan_racPOST:(NSString *)URLString parameters:(nullable id)parameters headers:(nullable NSDictionary<NSString *,NSString *> *)headers constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block progress:(void (^)(NSProgress * _Nonnull))uploadProgress;

m文件可以在源碼里面查看

3.5 ReactiveCocoa其他注意點(diǎn)和方法

我們知道RACCommand主要用于按鈕綁定事件,那么其他的UIKit常用框架都被RAC封裝了一遍,都有我們需要常用的信號(hào)量吉捶,使用起來(lái)可以通過(guò)一個(gè)按鈕夺鲜,和文本框,逐漸帚稠,由淺入深谣旁,深入了解ReactiveCocoa的強(qiáng)大床佳,和他的響應(yīng)式設(shè)計(jì)理念滋早,及配合MVVM框架使用方法

  • FanAPIManager可以看下這個(gè)類(lèi)封裝,就是一個(gè)基本網(wǎng)絡(luò)庫(kù)的封裝砌们,可以繼承他杆麸,實(shí)現(xiàn)比如分模塊處理,用戶(hù)登錄浪感,其他模塊昔头,可以把網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)解析的動(dòng)作封裝進(jìn)入,用戶(hù)信號(hào)訂閱的直接是需要的數(shù)據(jù)影兽,或者需要的錯(cuò)誤數(shù)據(jù)(當(dāng)然需要后臺(tái)配合寫(xiě)一條完善的接口揭斧,不然亂七八糟,不容易封裝)
  • rac_signalForSelector:用于替代代理峻堰。
  • rac_valuesAndChangesForKeyPath:用于監(jiān)聽(tīng)某個(gè)對(duì)象的屬性改變讹开。
  • rac_signalForControlEvents:用于監(jiān)聽(tīng)某個(gè)事件。
  • rac_addObserverForName:用于監(jiān)聽(tīng)某個(gè)通知捐名。
  • rac_textSignal:只要文本框發(fā)出改變就會(huì)發(fā)出這個(gè)信號(hào)旦万。
  • rac_liftSelector:withSignalsFromArray:Signals:當(dāng)傳入的Signals(信號(hào)數(shù)組),每一個(gè)signal都至少sendNext過(guò)一次镶蹋,就會(huì)去觸發(fā)第一個(gè)selector參數(shù)的方法成艘。
    使用注意:幾個(gè)信號(hào)赏半,參數(shù)一的方法就幾個(gè)參數(shù),每個(gè)參數(shù)對(duì)應(yīng)信號(hào)發(fā)出的數(shù)據(jù)淆两。

更新歷史(Version Update)

Release 0.0.1

  • 通過(guò)簡(jiǎn)單的用戶(hù)登錄接口實(shí)現(xiàn) 網(wǎng)絡(luò)信號(hào)封裝断箫,VM封裝

Like(喜歡)

有問(wèn)題請(qǐng)直接在文章下面留言,喜歡就給個(gè)Star(小星星)吧!

Email:fqsyfan@gmail.com

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末秋冰,一起剝皮案震驚了整個(gè)濱河市瑰枫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌丹莲,老刑警劉巖光坝,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異甥材,居然都是意外死亡盯另,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)洲赵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鸳惯,“玉大人,你說(shuō)我怎么就攤上這事叠萍≈シⅲ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵苛谷,是天一觀的道長(zhǎng)辅鲸。 經(jīng)常有香客問(wèn)我,道長(zhǎng)腹殿,這世上最難降的妖魔是什么独悴? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮锣尉,結(jié)果婚禮上刻炒,老公的妹妹穿的比我還像新娘。我一直安慰自己自沧,他們只是感情好坟奥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著拇厢,像睡著了一般爱谁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上旺嬉,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天管行,我揣著相機(jī)與錄音,去河邊找鬼邪媳。 笑死捐顷,一個(gè)胖子當(dāng)著我的面吹牛荡陷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播迅涮,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼废赞,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了叮姑?” 一聲冷哼從身側(cè)響起唉地,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎传透,沒(méi)想到半個(gè)月后耘沼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡朱盐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年群嗤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兵琳。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡狂秘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出躯肌,到底是詐尸還是另有隱情者春,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布清女,位于F島的核電站钱烟,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏校仑。R本人自食惡果不足惜忠售,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一传惠、第九天 我趴在偏房一處隱蔽的房頂上張望迄沫。 院中可真熱鬧,春花似錦卦方、人聲如沸羊瘩。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)尘吗。三九已至,卻和暖如春浇坐,著一層夾襖步出監(jiān)牢的瞬間睬捶,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工近刘, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留擒贸,地道東北人臀晃。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像介劫,于是被迫代替她去往敵國(guó)和親徽惋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

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