数据传递

ReactiveCocoa理解笔记(2)

2016-01-13  本文已影响737人  周末年安

MVVM和ReactiveCocoa

在MVVM中UIViewController讲专注的负责View的管理、界面流程跳转,它不会直接持有、处理数据。UIViewController需要准备数据来组织、展现View,这时它需要通过对应的View-Model来准备,数据的获取、处理、持有都由View-Model来负责。估计这时大多数人心里有有了一些想法:View-Model在处理准备好数据以后,完全可以通过代理、block来实现通知UIViewController刷新界面。这个想法也是可以的,不使用ReactiveCocoa也可以实现MVVM开发模式,但是我还是愿意花时间来学习、了解ReactiveCocoa。初始接触ReactiveCocoa时,感觉它是一个很怪异的东东,以前“正”着写的,现在需要“反”着写,语法怪异,理解困难。其他朋友们有必要花时间学习吗?

目前有一个很火的理念:数据与视图绑定。就是当数据变化时,视图不需要额外的处理,便可正确地呈现最新的数据。而这也是ReactiveCocoa最重要的亮点。RAC与MVVM编程模式结合,可以方便的处理界面变化,减少View-ModelUIViewController连接,让我们两层的核心功能。

ReactiveCocoa不是单一功能的模块,它是一个Framework,提供了一整套解决方案。其核心思想是「响应数据的变化」,在这个基础上有了Signal的概念,进而可以帮助减少状态类型,易于使用MVVM架构,方便的响应式编程等等。

为什么要使用ReactiveCocoa

  1. 开发过程中,状态以及状态之间依赖过多,RAC更加有效率地处理事件流,而无需显式去管理状态。在过程式编程中,状态变化是最难跟踪,最头痛的事。这个是我使用RAC最重要的一点。

比如:我们想要实现一个需求:当“count”变量中的字符串改变后即时做出相应的反馈。用KVO我们一般会这样做

<pre>`
// In your class viewDidLoad/init
[self addObserver:self
forKeyPath:@"count"
options:NSKeyValueObservingOptionNew
context:nil];

// In dealloc
[self removeObserver:self
forKeyPath:@"count"
context:nil];

// rewrite in your class

如果我们在工程中使用ReactiveCocoa后,只有短短几行,还无需重写方法
<pre>[RACObserve(self, count) subscribeNext:^(NSNumber *count) { //do .... }];</pre>

看到RAC的实现是不是觉得很简单,代码简洁清晰,下面了解一下RAC

RACSignal

RAC为应用中发生的不同事件流提供了一个标准接口。在ReactiveCocoa术语中这个叫做信号,由RACSignal类表示。RAC的核心就是RACSignal发送事件流给它的订阅者,共有三种类型的事件:nexterrorcompleted。一个signal可以在completed前发送任意数量的next事件;也会因为error终止。简单的说就是errorcompleted只能发送一次,在completed前可以发送多次next事件。

信号订阅

信号是一个发送一连串信息的载体体. 如果它还没有订阅者,那么它就是一个冷信号,现在不会起任何作用。只有信号被订阅了,它才会向它的订阅者发送信息,这时信号处于激活状态,称之为热信号

冷信号默认什么也不干,所以我们需要避免在开发过程中创建一堆冷信号,比如下面这段代码,如果不被订阅,或者不被连接,那么它没有任何意义。
<pre>RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id subscriber) { NSLog(@"signal"); [subscriber sendNext:@"1"]; [subscriber sendNext:@"2"]; [subscriber sendCompleted]; return nil; }];</pre>

我们已经创建了一个冷信号signal,但因为没有被subscribe或连接,所以什么也不会发生。
加了下面这段代码后,signal就处于激活状态了,block里的代码就会被执行。

订阅示例:
<pre>[signal subscribeNext:^(NSString *name):^{ NSLog(@"name = %@", name); }];</pre>

连接示例:
<pre>RAC(self.usernameField, text) = RACObserve(self, name);</pre>
RACSignal有很多方法可以来订阅不同的事件类型。每个方法都需要至少一个block,当事件发生时就会执行block中的逻辑。比如每次next事件发生时,subscribeNext:方法提供的block都会执行。

<blockquote>

</blockquote>

ReactiveCocoa框架使用category来为很多基本UIKit控件添加signal。这样你就能给控件添加订阅了,如UITextField添加了rac_textSignal

创建RACSignal

使用

+(RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe

方法创建信号,在block需要实现“RACSubscriber”协议,最后还需返回RACDisposable,用于处理、销毁信号。

<pre>`
RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id subscriber) {
NSLog(@"signal");
[subscriber sendNext:@"1"];
[subscriber sendNext:@"2"];
[subscriber sendCompleted];
return nil;
}];

[signal subscribeNext:^(NSString *name):^{
NSLog(@"name = %@", name);
}];
`</pre>

使用“RACObserve”宏定义快捷创建信号
<pre>[RACObserve(self, name) subscribeNext:^(NSString *name) { NSLog(@"count %@", name); }];</pre>

这些是一些简单的用法,下面给一个实用的,在开发过程中可以实用的示例,这是一个根据URL获取数据的RACSignal创建示例:

<pre>`

pragma mark - Get请求

信号创建以后,订阅者可以获取数据
<pre>`
//创建获取数据的信号
RACSignal *signal = [self fetchGetServerFromURL:[NSURL URLWithString:@"http://www.sina.com"]];

//订阅信号,编写信号数据流处理
RACDisposable *disposable = [signal subscribeNext:^(id x) {
NSLog(@"data= %@", x);
} error:^(NSError *error) {
NSLog(@"error = %@", error);
}];
`</pre>

在请求的过程中,我们如果不需要了,也可以取消请求
<pre>`
//创建获取数据的信号
RACSignal *signal = [self fetchGetServerFromURL:[NSURL URLWithString:@"http://www.sina.com"]];

//订阅信号,编写信号数据流处理
RACDisposable *disposable = [signal subscribeNext:^(id x) {
NSLog(@"data= %@", x);
} error:^(NSError *error) {
NSLog(@"error = %@", error);
}];

//取消请求
if (!disposable.isDisposed) {
[disposable dispose];
}
`</pre>

当我们手动销毁信号,这时之前创建信号时生成的RACDisposable对象就会生效
<pre>return [RACDisposable disposableWithBlock:^{ //信号销毁时,取消请求 [operation cancel]; }];</pre>

这一连串代码示例就是信号创建-订阅-处理(或取消)的过程,只要先理解清楚信号的工作原理,在后面的学习、应用中,会发现RAC更加便捷,更加简单。

总结

这张主要说了RAC的优点以及基本用法,这些基本用法能应对开发过程中各种复杂的需求吗,下面会说说RAC的一下高级用法。

上一篇 下一篇

猜你喜欢

热点阅读