@@ -145,30 +145,30 @@ self.apiHelper = [[PXAPIHelper alloc]
145145+ (RACSignal *)importPhotos{
146146 RACReplaySubject * subject = [RACReplaySubject subject];
147147 NSURLRequest * request = [self popularURLRequest];
148- [NSURLConnection sendAsynchronousRequest:request
149- queue:[NSOperationQueue mainQueue]
148+ [NSURLConnection sendAsynchronousRequest:request
149+ queue:[NSOperationQueue mainQueue]
150150 completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError){
151151 if (data) {
152152 id results = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
153-
153+
154154 [subject sendNext:[[[results[@"photos"] rac_sequence] map:^id(NSDictionary *photoDictionary){
155155 FRPPhotoModel * model = [FRPPhotoModel new];
156-
156+
157157 [self configurePhotoModel:model withDictionary:photoDictionary];
158158 [self downloadThumbnailForPhotoModel:model];
159-
159+
160160 return model;
161161 }] array];
162-
162+
163163 [subject sendCompleted];
164164 }
165165 else{
166166 [subject sendError:connectionError];
167167 }
168168 }];
169-
169+
170170 return subject;
171-
171+
172172}
173173```
174174
@@ -184,7 +184,7 @@ self.apiHelper = [[PXAPIHelper alloc]
184184
185185这是你看到的异步操作中,一个非常普通的模式。
186186
187- 1 . 创建一个RACSubject.
187+ 1 . 创建一个RACSubject.
1881882 . 从异步调用的完成block中向RACSubject传送结果值。
1891893 . 立即返回这个RACSubject对象
190190
@@ -196,10 +196,10 @@ URL请求的构造方法看起来应该是这样的:
196196
197197```
198198+ (NSURLRequest *)popularURLRequest {
199- return [AppDelegate.apiHelper urlRequestForPhotoFeature:PXAPIHelperPhotoFeaturePopular
199+ return [AppDelegate.apiHelper urlRequestForPhotoFeature:PXAPIHelperPhotoFeaturePopular
200200 resultsPerPage:100 page:0
201- photoSize:PXPhotoModelSizeThumbnail
202- sortOrder:PXAPIHelperSortOrderRating
201+ photoSize:PXPhotoModelSizeThumbnail
202+ sortOrder:PXAPIHelperSortOrderRating
203203 except:PXPhotoModelCategoryNude];
204204}
205205```
@@ -211,10 +211,10 @@ if(data){
211211 [subject sendNext:[[[results[@"photos"] rac_sequence] map:^id (NSDictionary *photoDictionary){
212212 FRPPhotoModel *model = [FRPPhotoModel new];
213213 [self donwloadThumbnailForPhotoModel:model];
214-
214+
215215 return model;
216216 }] array]];
217-
217+
218218 [subject sendCompleted];
219219}
220220else{
@@ -263,9 +263,9 @@ else{
263263 photomodel.identifier = dictionary[@"id"];
264264 photomodel.photographerName = dictionary[@"user"][@"username"];
265265 photomodel.rating = dictionary[@"rating"];
266-
266+
267267 photomodel.thumbnailURL = [self urlForImageSize:3 inArray:dictionary[@"images"]];
268-
268+
269269 //Extended attributes fetched with subsequent request
270270 if (dictionary[@"comments_count"]){
271271 photomodel.fullsizedURL = [self urlForImageSize:4 inArray:dictionary[@"images"]];
@@ -299,7 +299,7 @@ else{
299299 - 第一步,我们过滤掉那些` size ` 字段不匹配要求的字典。
300300 - 然后,将这些符合要求的字典做一次映射来提取字典中` url ` 字段的内容。
301301 - 最后,我们获得一个NSString 对象的序列,把它转化为数组,然后返回` firstObject ` .
302-
302+
303303> 这里插图一个
304304
305305在ReactiveCocoa中类似上面的链式调用非常常见。值从` rac_sequence ` 推送到` filter: ` 方法中,最后推送到` map: ` 方法里。最后调用序列` rac_sequence ` 的` array ` 方法,将序列的结果转化为` array ` .
@@ -309,10 +309,10 @@ else{
309309```
310310+ (void)downloadThumbnailForPhotoModel:(FRPPhotoModel *)photoModel{
311311 NSAssert(photoModel.thumbnailURL, @"Thumbnail URL must not be nil");
312-
312+
313313 NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:photoModel.ThumbnailURL]];
314- [NSURLConnection sendAsynchronousRequest:request
315- queue:[NSOperationQueue mainQueue]
314+ [NSURLConnection sendAsynchronousRequest:request
315+ queue:[NSOperationQueue mainQueue]
316316 completionHandler:^(NSURLResponse *response, NSData *data, NSError * connectionError){
317317 photoModel.thumbnailData = data;
318318 }];
@@ -336,20 +336,20 @@ static NSString * CellIdentifier = @"Cell";
336336
337337- (void)viewDidLoad{
338338 [super ViewDidLoad];
339-
339+
340340 //Configure self
341341 self.title = @"Popular on 500px";
342-
342+
343343 //Configure View
344344 [self.collectionView registerClass:[FRPCell class] forCellWithReuseIdentifier:CellIdentifier];
345-
345+
346346 //Reactive Stuff
347347 @weakify(self);
348348 [RACObserver(self, photosArray) subscribeNext:^(id x){
349349 @strongify(self);
350350 [self.collectionView reloadData];
351351 }];
352-
352+
353353 //Load data
354354 [self loadPopularPhotos];
355355}
@@ -396,7 +396,7 @@ static NSString * CellIdentifier = @"Cell";
396396- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
397397 FRPCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
398398 [cell setPhotoModel:self.photosArray[indexPath.row]];
399-
399+
400400 return cell;
401401}
402402```
@@ -423,23 +423,23 @@ static NSString * CellIdentifier = @"Cell";
423423
424424@end
425425```
426-
426+
427427这里有两个属性:一个图片视图和一个订阅者。图片视图是弱引用,因为它属于父视图(这是UICollectionViewCell的一个标准的用法),我们将实例化并赋值给imageView。接下来的属性是一个订阅,当使用ReactiveCocoa来设置图像视图的图像属性时,我们将接触到它。注意它必须是强引用而非弱引用否则你会得到一个运行时的异常。
428428
429429```
430430- (id)initWithFrame:(CGRect)frame{
431431 self = [super initWithFrame:frame];
432432 if(!self) return nil;
433-
433+
434434 //Configure self
435435 self.backgroundColor = []UIColor darkGrayColor];
436-
436+
437437 //Configure subviews
438438 UIImageView * imageView = [[UIImageView alloc] initWithFrame:self.bounds];
439439 imageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
440440 [self.contentView addsubView:imageView];
441441 self.imageView = imageView;
442-
442+
443443 return self;
444444}
445445```
@@ -449,7 +449,7 @@ static NSString * CellIdentifier = @"Cell";
449449
450450```
451451- (void)setPhotoModel:(FRPPhotoModel *)photoModel{
452- self.subscription = [[[RACObserver(photoModel, thumbnailData)
452+ self.subscription = [[[RACObserver(photoModel, thumbnailData)
453453 filter:^ BOOL (id value){
454454 return value != nil;
455455 }] map:^id (id value){
@@ -463,11 +463,11 @@ static NSString * CellIdentifier = @"Cell";
463463
464464 1. 当它没有接受一个新的值时,我们想延迟处理。
465465 2. 信号的订阅通常是冷信号,除非有人订阅他(信号),否则信号不会起作用。
466-
466+
467467` setKeyPath:onObject: ` 是` RACSignal ` 的一个方法:绑定最新的信号的值给对象的关键路径。在这里我们在一个级联的信号上调用了这个方法,让我们来仔细看看:
468468
469469```
470- [[RACObserver (photoModel, thumbnailData)
470+ [[RACObserver (photoModel, thumbnailData)
471471 filter:^BOOL (id value){
472472 return value != nil;
473473 }] map:^ id (id value){
@@ -477,4 +477,30 @@ static NSString * CellIdentifier = @"Cell";
477477
478478> 插入一个插图
479479
480- Page47--待续
480+ 信号由` RACObserver ` 这个C的宏生成,这个宏简单地返回一个监控目标对象关键路径值变化的信号。在我们这个例子中,我们的目标对象是` photoModel ` ,关键路径为` thumbnailData ` 属性。我们过滤掉所有的nil值,然后对过滤后的值做映射:把NSData实例转为UIImage对象。
481+
482+ 注意,把NSData实例转化为UIImage的这个映射仅在小图上可以很好地运行,如果频繁地做这个映射或者作用到大图上会引起性能问题。理想的情况下,我们会缓存这些已经解压的图像以避免每一次都重复计算。这个技术不是本书所讨论的范畴,但我们将使用另一个通过ReactiveCocoa来实现的方法。
483+
484+ thumbnailData属性根本不需要在这里设置,他可以在稍后的某个时间在应用的其他部分来完成设置,然后cell的图像就会像魔术一般更新。
485+
486+ 可以让我们稍微突破一下Model-View-Controller模式好吗?只是一点点的不守规矩。幸运的是,下一章我们将看到无处不在的MVC模式的困境,所以我们不必担心这一点点的突破,一点点的改进。
487+
488+ 上面提到的` setKeyPath:onObject: ` 方法中,一旦` onObject: ` 对象被释放,他的订阅也会被自动取消。我们的cell实例是被collectionView所复用的,因此在复用的时候,我们需要取消cell上各组件的订阅。我们可以通过重写` UICollectionViewCell ` 的下列方法达成:
489+
490+ ```
491+ - (void)perpareForReuse {
492+ [super prepareForReuse];
493+
494+ [self.subscription dispose], self.subscription = nil;
495+ }
496+
497+ ```
498+
499+ 这个方法在Cell被复用之前调用。如果现在运行我的应用,我们可以看到下面的结果:
500+
501+ > 插图
502+
503+ 太好了!我们可以通过滚动视图来证实我们手动处理订阅的有效性。
504+
505+
506+
0 commit comments