ReactiveCocoa源码拆分解析(四)
(整个关于ReactiveCocoa的代码工程可以在https://github.com/qianhongqiang/QHQReactive下载)
上一章节简要的说明了如何实现的热信号。但是像那么写,貌似不是非常优雅。这一章节我们会把冷热信号转换写的跟ReactiveCocoa一样优雅。
ReactiveCocoa内部是如何实现冷热信号转换的呢?我们来看个例子
RACSignal *replayLazilySignal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"replaylazily---sendAction");
[subscriber sendNext:@"replaylazily"];
return nil;
}] replayLazily];
[replayLazilySignal subscribeNext:^(id x) {
NSLog(@"subscribe1----%@",x);
}];
[replayLazilySignal subscribeNext:^(id x) {
NSLog(@"subscribe2----%@",x);
}];
没错,只是在原有信号的基础上,增加了一个replayLazily方法掉用。我们从输出上可以看看,是否转为了热信号。
2015-12-29 13:28:03.588 xxxxxx[40933:6054173] <xxxxxx:(232)> replaylazily---sendAction
2015-12-29 13:28:03.589 xxxxxx[40933:6054173] <xxxxxx:(238)> subscribe1----replaylazily
2015-12-29 13:28:03.589 xxxxxx[40933:6054173] <xxxxxx:(242)> subscribe2----replaylazily
发送只有1次,被订阅了两次。那么我们也朝这个目标出发。
我们这次让一个热信号QHQSubject去订阅最初的冷信号,偷偷的将返回改成这个热信号,偷梁换柱而神不知鬼不觉。
-(QHQSignal *)replayLazily {
QHQMulticastConnection *conn = [[QHQMulticastConnection alloc] initWithSourceSignal:self outSignalSubject:[[QHQSubject alloc] init]];
[conn connectSignal];
return conn.connSignal;
}
这里用到了一个新类,用于做这层转化QHQMulticastConnection,它需要一个源,和一个订阅信号。当执行connectSignal方法后,让热信号订阅最初信号。
-(void)connectSignal {
[_sourceSignal subscribe:_connSignal];
}
让热信号称为接下来流的来源。看似已经天衣无缝了,执行下,看看结果如何
-(void)demoFourReplayLazily {
QHQSignal *replaySignal = [[QHQSignal createSignal:^(id subscriber) {
[subscriber sendNext:@"replaySignal---send"];
}] replayLazily];
[replaySignal subscribeNext:^(id x) {
NSLog(@"sub1 ---- %@",x);
}];
[replaySignal subscribeNext:^(id x) {
NSLog(@"sub2 ---- %@",x);
}];
}
2015-12-29 13:39:59.345 PageText[42204:6070078] replaySignal----send
怎么没有输出订阅的结果呢?确实不应该输出结果,开始分析下原因。当你掉用replayLazily方法时,你已经偷偷摸摸将信号换成了QHQSubject,在这个过程中,你已经订阅了信号,也就是最终的输出结果。让后你再去订阅这个QHQSubject的时候,它做不了任何事情,因为信号的发送已经过去了,过去了,了。我们可以做个延迟发送事件看看。
QHQSignal *replaySignal = [[QHQSignal createSignal:^(id subscriber) {
NSLog(@"replaySignal----send");
[subscriber sendNext:@"replaySignal---send"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"Delay-----replaySignal----send");
[subscriber sendNext:@"Delay-----replaySignal---send"];
});
}] replayLazily];
[replaySignal subscribeNext:^(id x) {
NSLog(@"sub1 ---- %@",x);
}];
[replaySignal subscribeNext:^(id x) {
NSLog(@"sub2 ---- %@",x);
}];
发送了两次事件,一次是理解发送,另一次做了延迟。看看结果
2015-12-29 13:45:35.560 PageText[42399:6074028] replaySignal----send
2015-12-29 13:45:37.561 PageText[42399:6074028] Delay-----replaySignal----send
2015-12-29 13:45:37.562 PageText[42399:6074028] sub1 ---- Delay-----replaySignal---send
2015-12-29 13:45:37.562 PageText[42399:6074028] sub2 ---- Delay-----replaySignal---send
延迟发送的事件确实收到了,说明确实转化成了热信号。但是我们总不能每次都延迟一下吧,当然我们可以搞定这个问题,我们让热信号把事件存起来,订阅这订阅后把事件都发送出去。这个时候需要一个可以保存事件的热信号。
因此,我们创建了一个新类继承于QHQSubject
@interface QHQReplaySubject ()
@property (nonatomic, assign) NSUInteger capacity;
@property (nonatomic, strong) NSMutableArray *values;
@end
它会将每次发送来的事件保存在values数组中,如果数组容量大于承载capacity,将会移除掉更早的事件
-(void)sendNext:(id)next {
[_values addObject:next];
[super sendNext:next];
if (_values.count >_capacity) {
[_values removeObjectAtIndex:0];
}
}
它每次被订阅时,都需要首先将已经保存的信号发送给订阅着
-(void)subscribe:(id<QHQSubscrib>)sub {
for (id value in _values) {
[sub sendNext:value];
}
[self.subscribers addObject:sub];
}
这样,第一次的信号就不会丢失。
-(QHQSignal *)replayLazily {
QHQMulticastConnection *conn = [[QHQMulticastConnection alloc] initWithSourceSignal:self outSignalSubject:[QHQReplaySubject replaySubjectWithCapacity:1]];
[conn connectSignal];
return conn.connSignal;
}
简单的将热信号替换成能够保存1个老信号的热信号,那么问题迎刃而解
2015-12-29 14:02:00.766 PageText[43381:6084580] replaySignal----send
2015-12-29 14:02:00.767 PageText[43381:6084580] sub1 ---- replaySignal---send
2015-12-29 14:02:00.767 PageText[43381:6084580] sub2 ---- replaySignal---send
2015-12-29 14:02:02.768 PageText[43381:6084580] Delay-----replaySignal----send
2015-12-29 14:02:02.769 PageText[43381:6084580] sub1 ---- Delay-----replaySignal---send
2015-12-29 14:02:02.770 PageText[43381:6084580] sub2 ---- Delay-----replaySignal---send
输出符合预期
实际上,RAC也是这么做的,不过它将所有的接口都处理成线程安全的。
ReactiveCocoa源码拆分解析(四)的更多相关文章
- ReactiveCocoa源码拆分解析(一)
(整个关于ReactiveCocoa的工程可以在https://github.com/qianhongqiang/QHQReactive下载) ReactiveCocoa的介绍我就不说了,可以自行百度 ...
- ReactiveCocoa源码拆分解析(二)
(整个关于ReactiveCocoa的代码工程可以在https://github.com/qianhongqiang/QHQReactive下载) 上面抽丝剥茧的把最主要的信号机制给分离开了.但在RA ...
- ReactiveCocoa源码拆分解析(七)
(整个关于ReactiveCocoa的代码工程可以在https://github.com/qianhongqiang/QHQReactive下载) 在这篇博客中,我将把ReactiveCocoa中的擦 ...
- ReactiveCocoa源码拆分解析(六)
(整个关于ReactiveCocoa的代码工程可以在https://github.com/qianhongqiang/QHQReactive下载) RAC为了实现优雅的信号绑定,可谓使尽浑身解数,不仅 ...
- ReactiveCocoa源码拆分解析(五)
(整个关于ReactiveCocoa的代码工程可以在https://github.com/qianhongqiang/QHQReactive下载) 好多天没写东西了,今天继续.主要讲解RAC如何于UI ...
- ReactiveCocoa源码拆分解析(三)
(整个关于ReactiveCocoa的代码工程可以在https://github.com/qianhongqiang/QHQReactive下载) 这一章节主要讨论信号的“冷”与“热” 在RAC的世界 ...
- mybatis 3.x源码深度解析与最佳实践(最完整原创)
mybatis 3.x源码深度解析与最佳实践 1 环境准备 1.1 mybatis介绍以及框架源码的学习目标 1.2 本系列源码解析的方式 1.3 环境搭建 1.4 从Hello World开始 2 ...
- Spring框架之spring-web web源码完全解析
Spring框架之spring-web web源码完全解析 spring-web是Spring webMVC的基础,由http.remoting.web三部分组成,核心为web模块.http模块封装了 ...
- 【原】AFNetworking源码阅读(四)
[原]AFNetworking源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇还遗留了很多问题,包括AFURLSessionManagerTaskDe ...
随机推荐
- java线程池(newSingleThreadExecutor())小应用
创建单个线程,用来操作一个无界的队列任务,不会使用额外的线程.如果线程崩溃会重新创建一个,直到任务完成. 代码: import java.util.concurrent.ExecutorService ...
- ubuntu 14.04 以root权限启动chrome
chrome版本 51.0.2704.103 How to run google chrome as root in linux - Unix & Linux Stack Exchange提示 ...
- JS文件加载:比较async和DOM Script
async与script动态加载都能使文件异步加载,本文叙述它们对页面渲染和load加载的影响方面. 目前我用demo.js作为执行文件操作.代码: var now = function() { re ...
- 马后炮之12306抢票工具(四)--抢票Demo,2014年1月9日终结版
时隔一年多,终于朋友的忽悠下吧抢票Demo的最后一步完善了,与2014年1月9日成功生成车票. Demo仅经过自己测试,并未在高峰期进行测试,代码质量很差,因为赶工,套用去年模板并未使用设计模式. 代 ...
- 大流量网站性能优化:一步一步打造一个适合自己的BigRender插件
BigRender 当一个网站越来越庞大,加载速度越来越慢的时候,开发者们不得不对其进行优化,谁愿意访问一个需要等待 10 秒,20 秒才能出现的网页呢? 常见的也是相对简单易行的一个优化方案是 图片 ...
- 千万别把WIFI玩坏了!关于WIFI的新鲜玩法和商业模式探讨
使用WIFI作为无线上网接入,也许大家都比较习以为常的.但是你们知道吗,其实WIFI还有很多种玩法:基于WIFI的身份识别,WIFI感知,WIFI Direct等等.今天我们会着重介绍一种WIFI的有 ...
- C 语言学习:班级同学的博客地址列表
这里整理出本班级同学的博客地址.请各位同学之间相互帮助,尽快将博客都建立起来. 编号 学号 姓名 Cnblogs昵称 博客地址 Coding.net 地址 1 160809401 付胤 付胤 http ...
- RecyclerView的使用(二)
上篇博客讲了如何导入RecyclerView . 本章将告诉小伙伴们怎么使用RecyclerView : 第一步:在布局中使用RecyclerView并且绑定控件: 第二步:自定义Adapter:(继 ...
- Deepin 15.3 编译同步SudaMod 2.0
1.设置hosts,方便访问国外站点 $ wget https://raw.githubusercontent.com/racaljk/hosts/master/hosts -qO /tmp/host ...
- 【Codeforces720D】Slalom 线段树 + 扫描线 (优化DP)
D. Slalom time limit per test:2 seconds memory limit per test:256 megabytes input:standard input out ...