@@ -55,5 +55,112 @@ Completion blocks?这是另外一个使用Signals的机会。更深入一点来
5555```
5656这里有两个大的不同:
5757
58- 1 . asdf
59- 2 . asdf
58+ 1 . 我们使用RAC来绑定` downloadFullsizedImageForPhotoModel: ` 返回的信号的最新值。
59+ 2 . 我们返回` NSURLConnection的rac_sendAsynchronousRequest: ` 返回值的映射。
60+
61+ 我们来看看这里究竟发生了什么。
62+ 看文档:` rac_sendAsynchronousRequest: ` 返回一个发送网络请求响应值的信号。` RACTuple ` 它所发送的内容分别包含响应和数据。有网络错误发生时,它会抛出错误。 最后我们改变线程的调度,将signal切换到主线程上。 (一个线程的调度者类似于一个线程。)
63+
64+ 看,网络信号将会把它的值返回给后台的调度者,如果我们不阻止它,它可能最终会去从事更新UI的事件,而后台线程是没有能力更新UI的。
65+
66+ 我们回过头来看看最开始的那两行。注意下这行:
67+
68+ ``` Objective-C
69+ RAC (photoModel, thumbnailData) = [ self download: photoModel .thumbnailURL] ;
70+ ```
71+
72+ 通常,我不推荐将一个model绑定到多个signal,然而,我们知道这个信号会在完成网络调用后立即执行完并结束订阅。只要我们仅在一个实例上绑定这个keyPath,这种就是安全的。
73+
74+ 我们可以用类似的方式抽象掉使用`RACReplaySubject`的部分,来重新审视我们的`fetchPhotoDetails:`方法吧。
75+
76+ ```
77+ + (RACReplaySubject * )fetchPhotoDetails:(FRPPhotoModel * )photoModel {
78+ RACReplaySubject *subject = [RACReplaySubject subject];
79+
80+ NSURLRequest *request = [self photoURLRequest:photoModel];
81+ [NSURLConnection sendAsynchronousRequest:request
82+ queue:[NSOperationQueue mainQueue]
83+ completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
84+ if(data) {
85+ id results = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil][@"photo"];
86+ [self configurePhotoModel:photoModel withDictionay:results];
87+ [self downloadFullsizedImageForPhotoModel:photoModel];
88+ [subject sendNext:photoModel];
89+ [subject sendCompleted];
90+ }
91+ else {
92+ [subject sendError:connectionError];
93+ }
94+ }];
95+
96+
97+ return subject;
98+
99+ }
100+ ```
101+
102+ 有一点点凌乱,我们来整理下。
103+
104+
105+ ```Objective-C
106+ + (RACSignal *)fetchPhotoDetails:(FRPPhotoModel *)photoModel {
107+ NSURLRequest *request = [self photoURLRequest:photoModel];
108+ return [[[[[[NSURLConnection rac_sendAsynchronousRequest:request]
109+ map:^id(RACTuple *value){
110+ return [value second];
111+ }]
112+ deliverOn:[RACScheduler mainThreadScheduler]]
113+ map:^id (NSData *data) {
114+ id results = [NSJSONSerialization JSONObjectWithData:data
115+ options:0 error:nil][@"photo"];
116+ [self configurePhotoModel:photoModel withDictionary:results];
117+ [self downloadFullsizedImageForPhotoModel:photoModel];
118+ return photoModel;
119+ }] publish] autoconnect];
120+ }
121+ ```
122+
123+ ** 注意:** 返回值从` RACReplaySubject * ` 变成了` RACSignal * ` .
124+ 这里有很多地方需要梳理,所以我们提前做了下面这个示意图来说明:
125+
126+ ![ RACSignal_Process_Diagram] ( ../images/racsignal_process_diagram.png )
127+
128+ 我们已经知道` deliverOn: ` 是怎样工作的,所以让我们来关注信号链条最末端的信号操作` publish ` . ` publish ` 返回一个` RACMulitcastConnection ` ,当信号连接上时,他将订阅该接收信号。` autoconnect ` 为我们做的是:当它返回的信号被订阅,连接到
129+ 该(订阅背后的)信号(underly signal)。
130+
131+ 执行获取每一个订阅,在订阅的时候,我们返回的信号将会变“冷”。那是因为我们对底层信号进行多播,网络请求只会执行一次,但是它的结果被多播。这会导致:网络信号将只会被执行一次(当它被订阅时执行),是冷的(直到订阅为止,它不会被执行),甚至可删除的(如果一次性处理订阅的生成)。
132+
133+ 基本上,我们能保证信号只会被订阅一次,我们不需要回滚(replay).
134+
135+ 注意:我们可以用下面的` reduceEach: ` 替代使用` RACTuple ` 的第一个` map: ` ,以便提供编译时检查。
136+
137+ ```
138+ reduceEach:^id(NSURLResponse *response, NSData *data) {
139+ return data;
140+ }]
141+ ```
142+
143+ 剩下的网络访问接口,` importPhotos ` 方法重构如下:
144+
145+ ```
146+ + (RACSignal *)importPhotos {
147+ NSURLRequest *request = [self popularURLRequest];
148+
149+ return [[[[[[NSURLConnection rac_sendAsynchronousRequest:request]
150+ reduceEach:^id(NSURLResponse *response , NSData *data){
151+ return data;
152+ }]
153+ deliverOn:[RACScheduler mainThreadScheduler]]
154+ map:^id (NSData *data) {
155+ id results = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
156+ return [[[results[@"photo"] rac_sequence]
157+ map:^id (NSDictionary *photoDictionary) {
158+ FRPPhotoModel *model = [FRPPhotoModel new];
159+ [self configurePhotoModel:model withDictionary:photoDictionary];
160+ [self downloadThumbnailForPhotoModel:model];
161+ return model;
162+ }] array];
163+ }] publish] autoconnect];
164+ }
165+ ```
166+
0 commit comments