常见的编程思想
- 面向过程
- 如C语言
- 面向对象
- 如Objective-C、C++、Java
- 链式编程
- Masonry:方法返回一个block,该block的参数为需要操作的参数,block的返回值为对象本身.
- 响应式编程(Reactive Programming):不考虑执行的顺序,只考虑结果
- KVO:KVO本质上是判断对象的属性有没有调用setter方法.
KVO底层实现:
- 生成对象的子类
- 重写setter方法,内部恢复父类方法,通知观察者
- 修改当前对象的isa指针,使其指向子类.使用objc_setClass(objc, class)来修改isa指针
- KVO:KVO本质上是判断对象的属性有没有调用setter方法.
- 函数式编程(Function Programming):把操作尽量写成一系列嵌套的函数或者方法调用
- ReactiveCocoa(RAC):被描述为函数响应式编程(FRP)框架, 它结合了函数式编程思想和响应式编程思想.
ReactiveCocoa的使用
ReactiveCocoa常见的类RACSignal:信号类,只要有数据改变,信号内部接收到数据,就会马上发出数据.不同的信号,订阅信号的处理是不同的.
1. 它本身不具备发送信号的能力,而是交给内部一个订阅者发出.
2. 默认一个信号是冷信号,也就是值改变了,不会触发,只有订阅了这个信号,才会变为热信号.
3. 使用RACSignal的subscribeNext方法就能订阅信号.
RACSubscriber:表示订阅者的意思,作用是发送数据.它是一个协议,不是一个类.
RACDisposable:用于取消订阅和清理资源
默认执行一次就取消订阅信号了,这个时候可以把订阅者改为强引用,只要订阅者在,就不会自动取消信号的订阅,使用RACDisposable的dispose方法自己主动取消订阅
1 | @property (nonatomic, strong)id<RACSubscriber> subscriber; |
RACSubject:信号提供者,自己可以当信号,又能订阅信号.
- 使用RACSubject来代替代理,设置成属性, 当想让其他类执行的时候就发送数据, 其他类就拿到这个属性订阅信号.这个时候就不用写协议和delegate了.
1 | //一、创建信号 |
- RACReplaySubject可以先发送信号,再订阅信号,而RACSubject不行
- 使用场景:
- 一个信号每被订阅一次,就把之前的值重复发一次
- 可以设置capacity的值来限制缓存value的值,即只保存最新的几个值
1 | //一、创建信号 |
1 | RACTuple *tuple = [RACTuple tupleWithObjects:@"1", @"2", @"3", nil]; |
RACSequence:RAC集合类,用来代替NSArray和NSDictionary,可以使用它来快速遍历数组和字典.
1 | //数组 |
ReactiveCocoa的在实际开发中的常见用法
- 代替代理:使用RACSubject代替代理,还可以使用rac_signalForSelector:@selector()方法把调用这个方法转换成信号
- 代替KVO
1
2
3
4
5
6
7
8[self rac_observeKeyPath:@"frame" options:(NSKeyValueObservingOptionNew) observer:nil
block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
//code
}];
//或者
[[self rac_valuesForKeyPath:@"frame" observer:nil] subscribeNext:^(id x) {
//code
}]; 监听事件
1
2
3
4UIButton *button = [UIButton buttonWithType:(UIButtonTypeCustom)];
[[button rac_signalForControlEvents:(UIControlEventTouchUpInside)] subscribeNext:^(id x) {
//code
}];代替通知
1
2
3
4[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification
object:nil] subscribeNext:^(id x) {
//code
}];- 监听文本框
1
2
3
4UITextField *textfield = [UITextField new];
[textfield.rac_textSignal subscribeNext:^(id x) {
//code
}]; - 处理同一个界面有多个请求接口,当所有接口都有数据的时候才处理一些事情,使用rac_liftSelector方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22- (void)moreAPI {
RACSignal *sellSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
//网络请求
[subscriber sendNext:@"获取动求购信息"];
return nil;
}];
RACSignal *buySignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
//网络请求
[subscriber sendNext:@"获取动求购信息"];
return nil;
}];
//数组用来存放 信号
//当数组中的所有信号都发送数据的时候,才会执行selector
//该方法的参数个数必须和数组中信号个数一致
[self rac_liftSelector:@selector(updateUIWithSellData: buyData:) withSignals:sellSignal, buySignal, nil];
}
//这个方法的参数就是信号发送的数据
- (void)updateUIWithSellData:(NSString *)sell buyData:(NSString *)buy {
NSLog(@"%@====%@", sell, buy);
}ReactiveCocoa中常见的宏
- RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于给某个对象的某个属性绑定。
1
2//输入框的文本和描述text绑定
RAC(self.descriptL, text) = self.nameL.rac_textSignal; - RACObserve(self, name):监听某个对象的某个属性,返回的是信号, 可以发送信号。
1
2
3[RACObserve(self, view.frame) subscribeNext:^(id x) {
NSLog(@"%@", x);
}]; - @weakify(Obj)和@strongify(Obj),一般两个都是配套使用,解决循环引用问题.
- RACTuplePack:把数据包装成RACTuple(元组类)
- RACTupleUnpack:把RACTuple(元组类)解包成对应的数据。
ReactiveCocoa实际使用
过滤
1 | //过滤使用filter来过滤 当输入框长度>=3的时候才会打印,可以直接修改block的参数类型, |
Map:用于把源信号内容映射成新的内容
1 | //使用map来改变输入数据的类型,输入框输入的是文本,改变为NSNumber类型 |
combineLatest:将多个信号合并起来,并且拿到各个信号的最新的值,必须每个合并的signal至少都有过一次sendNext,才会触发合并的信号。reduce聚合:用于信号发出的内容是元组,把信号发出元组的值聚合成一个值
1 | //登录按钮和 用户名和密码的长度绑定: 使用combineLatest和reduce |
1 | //使用 @weakify(self) 和 @strongify(self) 解决循环引用问题 |