原创:转载请注明出处

ReactiveCocoa / RxSwift

Native app有很大一部分的时间是在等待事件发生,然后响应事件,比如

1.等待网络请求完成,

2.等待用户的操作,

3.等待某些状态值的改变等等,

等这些事件发生后,再做进一步处理

但是这些等待和响应,并没有一个统一的处理方式。Delegate, Notification, Block, KVO, 常常会不知道该用哪个最合适。有时需要chain或者compose某几个事件,就需要多个状态变量,而状态变量一多,复杂度也就上来了。为了解决这些问题,Github的工程师们开发了ReactiveCocoa。

概念

插座:Signal   ->  插座负责去获取并提供电   <—>  Observable

插头:Subscriber  -> 插头负责使用电       <—> Observer

用途

Signal 获取到数据后,会调用Subscriber的sendNext, sendComplete, sendError方法来传送数据给Subscriber,

<>

RxSwift  : onNext   onComplete   onError

Subscriber 自然也有方法来获取传过来的数据,如:[signal subscribeNext:error:completed]。这样只要没有sendComplete和sendError,新的值就会通过sendNext源源不断地传送过来

<>

RxSwift:

reloadDataItem.rx_tap

.subscribeNext { [unowned self] in

self.initialization()

}.addDisposableTo(disposeBag)

RAC 是基于KVO的

原理:

RACObserve使用了KVO来监听property的变化,只要username被自己或外部改变,block就会被执行。但不是所有的property都可以被RACObserve,该property必须支持KVO,比如NSURLCache的currentDiskUsage就不能被RACObserve.

灵活性:

Signal是很灵活的,它可以被修改(map),过滤(filter),叠加(combine),串联(chain),这有助于应对更加复杂的情况,

RAC(self.logInButton, enabled) = [RACSignal

combineLatest:@[

self.usernameTextField.rac_textSignal,

self.passwordTextField.rac_textSignal,

RACObserve(LoginManager.sharedManager, loggingIn),

RACObserve(self, loggedIn)

] reduce:^(NSString *username, NSString *password, NSNumber *loggingIn, NSNumber *loggedIn) {

return @(username.length > 0 && password.length > 0 && !loggingIn.boolValue && !loggedIn.boolValue);         }];

1.只要usernameTextField的值有变化,这个值就会被返回(sendNext)。

2.combineLatest需要每个signal至少都有过一次sendNext。

3.reduce的作用是根据接收到的值,再返回一个新的值,这里是@(YES)和@(NO),必须是object。

<>

RxSwift:

Observable.combineLatest(calendarRequest, scheduleRequest, calendarVC.rx_didScrollToMonth) {

(scheduleList: $0, schedule: $1, didScroll:$2)

}

.onAlertError()

.subscribe(

onNext: { [unowned self] x in

//self.updateData(x.scheduleList, schedule: x.schedule, noticeList: nil)

},

onError: { [unowned self] error in

//

},

onCompleted: {

//

}

).addDisposableTo(self.disposeBag)

Side Effect

还是上面那段代码,如果有多个subscriber,那么signal就会又一次被触发,控制台里会输出两次triggered。这或许是你想要的,或许不是。如果要避免这种情况的发生,可以使用 replay 方法,它的作用是保证signal只被触发一次,然后把sendNext的value存起来,下次再有新的subscriber时,直接发送缓存的数据。

Cocoa Categories

UIView Categories

1. [[alertView rac_buttonClickedSignal] subscribeNext:^(NSNumber *indexNumber) {

//toDo

}];

RAC给UITableViewCell提供了一个方法:rac_prepareForReuseSignal,它的作用是当Cell即将要被重用时,告诉Cell。想象Cell上有多个button,Cell在初始化时给每个button都addTarget:action:forControlEvents,被重用时需要先移除这些target,下面这段代码就可以很方便地解决这个问题:

  1. [[[self.cancelButton
  2. rac_signalForControlEvents:UIControlEventTouchUpInside]
  3. takeUntil:self.rac_prepareForReuseSignal]
  4. subscribeNext:^(UIButton *x) {
  5. // do other things
  6. }];

rac_command,就是当button被按下时会执行的一个命令,命令被执行完后可以返回一个signal,有了signal就有了灵活性。比如点击投票按钮,先判断一下有没有登录,如果有就发HTTP请求,没有就弹出登陆框,可以这么实现。

NotificationCenter Category

NSNotificationCenter, 默认情况下NSNotificationCenter使用Target-Action方式来处理Notification,这样就需要另外定义一个方法,这就涉及到编程领域的两大难题之一:起名字。有了RAC,就有Signal,有了Signal就可以subscribe,于是NotificationCenter就可以这么来处理,还不用担心移除observer的问题。

  1. [[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"MyNotification" object:nil] subscribeNext:^(NSNotification *notification) {
  2. NSLog(@"Notification Received");
  3. }];

NSObject+RACDeallocating.h

顾名思义就是在一个objectdealloc被触发时,执行的一段代码。

  1. NSArray *array = @[@"foo"];
  2. [[array rac_willDeallocSignal] subscribeCompleted:^{
  3. NSLog(@"oops, i will be gone");
  4. }];
  5. array = nil;

NSObject+RACSelectorSignal.h

这个category有rac_signalForSelector:和rac_signalForSelector:fromProtocol: 这两个方法。先来看前一个,它的意思是当某个selector被调用时,再执行一段指定的代码,相当于hook。比如点击某个按钮后,记个日志。后者表示该selector实现了某个协议,所以可以用它来实现Delegate。

MVVM

MVVM跟MVC最大的区别是多了个ViewModel,它直接与View绑定,而且对View一无所知。拿做菜打比方的话,ViewModel就是调料,它不关心做的到底是什么菜。它可以扮演Model的职责,但其实它是Model的中介,这样当Model的API有变化,或者由本地存储变为远程API调用时,ViewModel的public API可以保持不变。

使用ViewModel的好处是,可以让Controller更加简单和轻便,而且ViewModel相对独立,也更加方便测试和重用。那Controller这时又该做哪些事呢?在MVVM体系中,Controller可以被看成View,所以它的主要工作是

处理布局、

动画、

接收系统事件、

展示UI。

MVVM还有一个很重要的概念是 data binding,view的呈现需要data,这个data就是由ViewModel提供的,将view的data与ViewModel的data绑定后,将来双方的数据只要一方有变化,另一方就能收到。这里有Github 开源的一个ViewModel Base Class。

其他

RAC在使用时有一些注意事项,可以参考官方的DesignGuildLines,这里简单说一下。

当一个signal被一个subscriber subscribe后,这个subscriber何时会被移除?答案是当subscriber被sendComplete或sendError时,或者手动调用[disposable dispose]。

当subscriber被dispose后,所有该subscriber相关的工作都会被停止或取消,如http请求,资源也会被释放。

Signal events是线性的,不会出现并发的情况,除非显示地指定Scheduler。所以-subscribeNext:error:completed:里的block不需要锁定或者synchronized等操作,其他的events会依次排队,直到block处理完成。

Errors有优先权,如果有多个signals被同时监听,只要其中一个signal sendError,那么error就会立刻被传送给subscriber,并导致signals终止执行。相当于Exception。

生成Signal时,最好指定Name, -setNameWithFormat: 方便调试。

block代码中不要阻塞。

ReactiveCocoa / RxSwift 笔记一的更多相关文章

  1. iOS开发ReactiveCocoa学习笔记(六)

    RAC操作方法三. demo地址:https://github.com/SummerHH/ReactiveCocoa.git doNext deliverOn timeout interval del ...

  2. iOS开发ReactiveCocoa学习笔记(五)

    ReactiveCocoa常见操作方法介绍: demo地址:https://github.com/SummerHH/ReactiveCocoa.git filter ignore ignoreValu ...

  3. iOS开发ReactiveCocoa学习笔记(四)

    ReactiveCocoa常见操作方法介绍: demo地址:https://github.com/SummerHH/ReactiveCocoa.git 1.1 ReactiveCocoa操作须知 所有 ...

  4. iOS开发ReactiveCocoa学习笔记(二)

    RAC 中常见的宏: 使用宏定义要单独导入 #import <RACEXTScope.h> 一. RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于给某个对象的某 ...

  5. iOS开发ReactiveCocoa学习笔记(一)

    学习 RAC 我们首先要了解 RAC 都有哪些类 RACSignal RACSubject RACSequence RACMulticastConnection RACCommand 在学习的时候写了 ...

  6. ReactiveCocoa学习笔记--用法

    1.监测UI变量的变化 return 后把值传递下去. 1.1.输出 [self.usernameTextField.rac_textSignal subscribeNext:^(id x){ NSL ...

  7. iOS开发ReactiveCocoa学习笔记(三)

    RAC常用用法: 1.监听按钮的点击事件: UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; button.frame ...

  8. ReactiveCocoa学习资料

    ReactiveCocoa 学习资料: ReactiveCocoa入门教程:第一部分 http://www.cocoachina.com/ios/20150123/10994.html Reactiv ...

  9. ReactiveCocoa 学习资料

    之前就有听说,感觉很强大,ReactiveCocoa更加被Mattt Thompson大神称为开启一个新Objective-C纪元.所以觉得非常有学习的必要了. 一些很好的学习资料: Reactive ...

随机推荐

  1. 各硬件设备在Linux中的文件名

  2. LeetCode OJ 152. Maximum Product Subarray

    Find the contiguous subarray within an array (containing at least one number) which has the largest ...

  3. Gentoo挂载ntfs的NTFS分区

    内核需要开启的选项 File systems ---> <*> FUSE (Filesystem in Userspace) support 使用NTFS-3G NTFS-3G是一个 ...

  4. VS2010编译以前版本工程时 ERROR CVT1100:duplicate resource,type:MANIFEST解决办法

    1.将 Resource Files 里面的 *.exe.manifest 文件删除 2.右键选择 *.rc 文件,选择 view code,查找并删除所有引用 *.exe.manifest 文件的代 ...

  5. python--sorted函数和operator.itemgetter函数

    1.operator.itemgetter函数operator模块提供的itemgetter函数用于获取对象的哪些维的数据,参数为一些序号(即需要获取的数据在对象中的序号),下面看例子. a = [1 ...

  6. 不合法语句 self.contentView.frame.origin.x = x;

    下面的写法是错误的: CGFloat x = self.contentView.frame.origin.x; x = _lastDownX + translation.x; self.content ...

  7. 如何用Navicat for MySQL 将mysql中的数据库导出,导入。

    数据库导出 1.双击要导出的数据库,右键选转储SQL文件...,选择要保存的文件夹. 2.点击开始后,开始导出. 数据库导入 1.新建数据库,数据库的名字必须和导入的数据库文件一致. 2.在新建的数据 ...

  8. Linux内核协议栈 NAT性能优化之FAST NAT

    各位看官非常对不起,本文是用因为写的,如果多有不便敬请见谅 代码是在商业公司编写的,在商业产品中也不能开源,再次抱歉   This presentation will highlight our ef ...

  9. Android Development HandBook-Android Studio 特别篇

    开发准备中http://www.cnblogs.com/dev2007/p/4059829.html 主要介绍了基础环境的搭建,开发工具主要是Eclipse,由于Android Studio使用越来越 ...

  10. mybatis---------insert,delete ,update的批量操作

    在数据量大的情况下,可以使用批量 提高性能 批量插入insert 方法一: <insert id="insertbatch" parameterType="java ...