ReactiveCocoa入门教程二

来源:互联网 发布:matlab for mac 2016 编辑:程序博客网 时间:2024/04/29 21:58

欢迎访问我的个人blog
https://ekulelu.github.io/

上一篇文章我们使用ReactiveCocoa搭建了一个简单的登录页面,这一次我们来搭建注册页面。

本篇代码在https://github.com/Ekulelu/ReactiveCocoaRegister

Account和password的有效规制还是和之前一样,长度必须大于5,否则输入框背景色是红色的,这一步直接用之前的代码就可以了。
注册按钮的响应和登录的响应应该是一样的逻辑,对于之前的伪代码来讲,也几乎没什么改动。
但是注册和登录最起码有个不同的东西,就是注册的用户名可能已经被占用,我们想用户在输入完了用户名之后,从后台对用户名是否可用进行判断,如果可用,才允许用户点击“注册”按钮。

仿照登录时候将登录方法封装成为信号的做法,或许可以把用户名检查的功能也封装成为一个信号,但我们这里尝试另外一种做法:使用RACSubject对象。RACSubject对象继承于RACSignal,相比于RACSignal,RACSubject具有主动发送信号值的功能。下面新建一个RACSubject对象。

RACSubject *isAccountAvailableSubject = [RACSubject subject];

这个信号代表了用户名是否可以用,且这个信号的值可以由代码主动去控制发出。注册按钮应该是用户名合法且可用,且密码合法的情况下才可以点击,所以把这3个信号combine一起

 RAC(self.registerBtn, enabled) = [RACSignal combineLatest:@[isAccountAvailableSubject, accountValidSignal,passwordVaildSignal]                      reduce:^id(NSNumber *accountUse, NSNumber* accountValid, NSNumber* passwordVaild){                          return @(accountUse.intValue == AccountStatusAvailable && accountValid.boolValue && passwordVaild.boolValue);    }];

我们在用户名框的旁边放置一个imageView,用它的背景颜色来表示这个用户名是否可用,绿色代表可用,蓝色代表正在查找,黄色代表不可用。我们可以将这个属性和刚刚创建的isAccountAvailableSubject联系起来。

RAC(self.accountAvailableImgView, backgroundColor) = [isAccountAvailableSubject     map:^id(NSNumber *value) {         switch (value.intValue) {             case AccountStatusAvailable:                 return [UIColor greenColor];                 break;             case AccountStatusChecking:                 return [UIColor blueColor];                 break;             case AccountStatusUnavailable:             default:                 return [UIColor yellowColor];                 break;         }     }];

其中有一些枚举定义如下:

typedef NS_ENUM(int, AccountStatus) {    AccountStatusAvailable = 0,    AccountStatusChecking = 1,    AccountStatusUnavailable = 2,};

如果用户名不合法,判断用户名是否可用的imageView应该不要显示出来

RAC(self.accountAvailableImgView, hidden) = [accountValidSignal map:^id(NSNumber *value) {        return @(!value.boolValue);    }];

剩下就是根据用户名输入去查询用户名是否合法了。这个方法我们已经写好,问题就在于它的调用时间,它应该是在用户名输入合法的时候调用,但如果去订阅accountValidSignal,那么在切换输入框的时候也会调用。所以不能直接订阅,这里再介绍一个方法

- (RACSignal *)sample:(RACSignal *)sampler;

[SingleA sample:SingleB]; 这个意思是只有当SingleB有信号的时候才会去获取SingleA最新值。我们可以把SingleA设为accountValidSignal,SingleB设为[self.accountTV.rac_textSignal distinctUntilChanged],
distinctUntilChanged表示信号的值比上一次的信号值有更新了才会发送。

如果不加distinctUntilChanged,那么焦点离开也会发出rac_textSignal

这样就会在只有用户名改变的时候才会去获取accountValidSignal的最新值,然后加上filter进一步对用户名是否合法再过滤就可以了。

[[[accountValidSignal sample:[self.accountTV.rac_textSignal distinctUntilChanged]]      filter:^BOOL(NSNumber *value) {         return value.boolValue;    }]    subscribeNext:^(id x) {        [isAccountAvailableSubject sendNext:@(AccountStatusChecking)];        [self checkAccount:self.accountTV.text complete:^(Boolean success) {            if (success) {                [isAccountAvailableSubject sendNext:@(AccountStatusAvailable)];                NSLog(@"can use accout");            } else {                [isAccountAvailableSubject sendNext:@(AccountStatusUnavailable)];                NSLog(@"can not use accout");            }        }];    }];

上面方法里面在回调里面使用isAccountAvailableSubject发送检查结果,accountAvailableImgView和注册按钮就会根据检查结果进行更新。
至此,一个简单的注册框就完成了。
当然这里面还有问题:假如第一次输入了一个合法的用户名,然后发起用户名检查,这时候立刻加上一个字符,那么又会发起第二次用户名检查,这个时候恰好第一次用户名检查的结果回来了,显示用户名可用,注册按钮可以点击。但是输入框里面已经是新的用户名了。这个问题留给大家思考。

循环引用

这个问题老生常谈了,有了block,就要小心这个问题。RAC采用了大量的block,一不小心就循环引用了。
为此,RAC里面提供了一个十分好用的宏来声明self的弱引用。这个宏定义在RACmetamacros.h里面。
这对宏是

@weakify(self) @strongify(self)

使用的方法如下,在block外边使用@weakify(self),然后在block里面使用@strongify(self)。以后block里面的self就是弱引用的了。

@weakify(self)RACSignal *accountValidSignal = [self.accountTV.rac_textSignal map:^id(id value) {    @strongify(self)    return @(self.accountTV.text.length > 5);}];

具体原理是weakify(self)声明了一个self的弱引用变量,strongify(self)把这个弱引用变量变为了局部的强引用变量。至于这个@,其实是autoreleasepool {} (在debug编译下) 或者 try {} @catch (…) {} (在release编译下)

0 0