学习戴铭博文《从 ReactiveCocoa 中能学到什么?不用此库也能学以致用》的总结
博文地址:https://ming1016.github.io/2016/05/30/what-learn-from-reactivecocoa/
之前一直在尝试着学习使用 RAC,由于水平有限暂时只达到会用一些简单API 的初级阶段,期间收集了很多RAC 方面的资料。
印象中戴铭老师的博客里面有好几篇关于RAC 的博文。加之这几天一直在看《Objective-C 高级编程iOS 与OS X 多线程和内存管理》这本书,阅读里面的关于block 原理部分,由于基础有限一时难以理解,思想停滞,遂看到戴铭老师的这篇博文,转换一下脑筋。
主要看一下里面根据 链式、函数式编程思想写的一个Demo:
首先是 SMCreditSubject 这个类似RAC 信号发送和订阅的类:
1 #import <Foundation/Foundation.h> 2 3 @interface SMCreditSubject : NSObject 4 5 typedef void(^SubscribeNextActionBlock)(NSUInteger credit); 6 7 + (SMCreditSubject *)create; 8 9 - (SMCreditSubject *)sendNext:(NSUInteger)credit; 10 - (SMCreditSubject *)subscribeNext:(SubscribeNextActionBlock)block; 11 12 @end
创建 Subject、Subject 发送(send)、Subject 订阅(subscribe)。使用过RAC 的开发者对这两个方法应该相当熟悉。
SubscribeNextActionBlock 有一个NSUInteger 类型的 credit 的参数,这个参数主要用于积分的传递。
1 #import "SMCreditSubject.h" 2 3 @interface SMCreditSubject() 4 5 @property (nonatomic, assign) NSUInteger credit; 6 @property (nonatomic, strong) SubscribeNextActionBlock subscribeNextBlock; 7 @property (nonatomic, strong) NSMutableArray *blockArray; 8 9 @end 10 11 @implementation SMCreditSubject 12 13 + (SMCreditSubject *)create { 14 SMCreditSubject *subject = [[self alloc] init]; 15 return subject; 16 } 17 18 - (SMCreditSubject *)sendNext:(NSUInteger)credit { 19 self.credit = credit; 20 if (self.blockArray.count > 0) { 21 for (SubscribeNextActionBlock block in self.blockArray) { 22 block(self.credit); 23 } 24 } 25 return self; 26 } 27 28 - (SMCreditSubject *)subscribeNext:(SubscribeNextActionBlock)block { 29 if (block) { 30 block(self.credit); 31 } 32 [self.blockArray addObject:block]; 33 return self; 34 } 35 36 #pragma mark - Getter 37 - (NSMutableArray *)blockArray { 38 if (!_blockArray) { 39 _blockArray = [NSMutableArray array]; 40 } 41 return _blockArray; 42 } 43 44 @end
属性里面有一个 blockArray 主要存放的每一个订阅的block,当 Subject 每被订阅(subscribe)一次,这个block 就会被添加进 blockArray 里面,当 Subject 发送(send)的时候,会对 blockArray 进行遍历,把里面的block 依次执行。且这些block 都是携带了一个 NSUInteger 的参数用于积分传递。
还有一点就是,每次订阅 Subject 的时候,这个订阅的 block 会先执行一次。即使 Subject 没有发送,也会首先执行一下这个block。
1 #import <Foundation/Foundation.h> 2 #import "SMCreditSubject.h" 3 4 typedef NS_ENUM(NSUInteger, SMStudentGender) { 5 SMStudentGenderMale, 6 SMStudentGenderFemale 7 }; 8 9 typedef BOOL(^SatisfyActionBlock)(NSUInteger credit); 10 11 @interface SMStudent : NSObject 12 13 @property (nonatomic, strong) SMCreditSubject *creditSubject; 14 15 @property (nonatomic, assign) BOOL isSatisfyCredit; 16 17 + (SMStudent *)create; 18 - (SMStudent *)name:(NSString *)name; 19 - (SMStudent *)gender:(SMStudentGender)gender; 20 - (SMStudent *)studentNumber:(NSUInteger)number; 21 22 //积分相关 23 - (SMStudent *)sendCredit:(NSUInteger(^)(NSUInteger credit))updateCreditBlock; 24 - (SMStudent *)filterIsASatisfyCredit:(SatisfyActionBlock)satisfyBlock; 25 26 @end
首先上面的 create/name/gender/studentNumber 方法返回值都是一个 SMStudent 实例。当要为 SMStudent 添加新的功能时,采用这种链式编程的思想更易于扩展。
下面的两个积分相关的方法是函数编程的例子,函数编程主要思路就是用有输入输出的函数作为参数将运算过程尽量写成一系列嵌套的函数调用。这里是采用了有参数和返回值的 block。
这个例子中,sendCredit 的block 函数参数会处理当前的积分这个数据然后返回给SMStudent 实例记录下来,filterIsASatisfyCredit 的block 函数参数会处理是否达到合格的积分判断,返回是或否的BOOL 值给SMStudent 实例记录下来。
1 #import "SMStudent.h" 2 3 @interface SMStudent() 4 5 @property (nonatomic, copy) NSString *name; 6 @property (nonatomic, assign) SMStudentGender gender; 7 @property (nonatomic, assign) NSUInteger number; 8 @property (nonatomic, assign) NSUInteger credit; 9 @property (nonatomic, strong) SatisfyActionBlock satisfyBlock; 10 11 @end 12 13 @implementation SMStudent 14 15 + (SMStudent *)create { 16 SMStudent *student = [[self alloc] init]; 17 return student; 18 } 19 - (SMStudent *)name:(NSString *)name { 20 self.name = name; 21 return self; 22 } 23 - (SMStudent *)gender:(SMStudentGender)gender { 24 self.gender = gender; 25 return self; 26 } 27 - (SMStudent *)studentNumber:(NSUInteger)number { 28 self.number = number; 29 return self; 30 } 31 32 - (SMStudent *)sendCredit:(NSUInteger (^)(NSUInteger credit))updateCreditBlock { 33 if (updateCreditBlock) { 34 self.credit = updateCreditBlock(self.credit); 35 if (self.satisfyBlock) { 36 self.isSatisfyCredit = self.satisfyBlock(self.credit); 37 if (self.isSatisfyCredit) { 38 NSLog(@"YES"); 39 } else { 40 NSLog(@"NO"); 41 } 42 } 43 } 44 return self; 45 } 46 47 - (SMStudent *)filterIsASatisfyCredit:(SatisfyActionBlock)satisfyBlock { 48 if (satisfyBlock) { 49 self.satisfyBlock = satisfyBlock; 50 self.isSatisfyCredit = self.satisfyBlock(self.credit); 51 } 52 return self; 53 } 54 55 #pragma mark - Getter 56 57 - (SMCreditSubject *)creditSubject { 58 if (!_creditSubject) { 59 _creditSubject = [SMCreditSubject create]; 60 } 61 return _creditSubject; 62 } 63 64 65 @end
1 //present 2 self.student = [[[[[SMStudent create] 3 name:@"ming"] 4 gender:SMStudentGenderMale] 5 studentNumber:345] 6 filterIsASatisfyCredit:^BOOL(NSUInteger credit){ 7 if (credit >= 70) { 8 self.isSatisfyLabel.text = @"合格"; 9 self.isSatisfyLabel.textColor = [UIColor redColor]; 10 return YES; 11 } else { 12 self.isSatisfyLabel.text = @"不合格"; 13 return NO; 14 } 15 16 }]; 17 18 @weakify(self); 19 [[self.testButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) { 20 @strongify(self); 21 22 [self.student sendCredit:^NSUInteger(NSUInteger credit) { 23 credit += 5; 24 NSLog(@"current credit %lu",credit); 25 [self.student.creditSubject sendNext:credit]; 26 return credit; 27 }]; 28 }]; 29 30 [self.student.creditSubject subscribeNext:^(NSUInteger credit) { 31 NSLog(@"第一个订阅的credit处理积分%lu",credit); 32 self.currentCreditLabel.text = [NSString stringWithFormat:@"%lu",credit]; 33 if (credit < 30) { 34 self.currentCreditLabel.textColor = [UIColor lightGrayColor]; 35 } else if(credit < 70) { 36 self.currentCreditLabel.textColor = [UIColor purpleColor]; 37 } else { 38 self.currentCreditLabel.textColor = [UIColor redColor]; 39 } 40 }]; 41 42 [self.student.creditSubject subscribeNext:^(NSUInteger credit) { 43 NSLog(@"第二个订阅的credit处理积分%lu",credit); 44 if (!(credit > 0)) { 45 self.currentCreditLabel.text = @"0"; 46 self.isSatisfyLabel.text = @"未设置"; 47 } 48 }];
虽然能看懂流程,但是还是无法体会到其中的益处,就是感觉很饶,饶恕我还是一个编程小白,我会加倍努力把它理解的。
END