RAC集合操作
前三篇我们已经基本了解RAC信号的创建,订阅和发送源码,今天我们来看一下关于RAC对集合做的自己的封装,看看它到底是如何进行操作的。
RAC数组操作
我们在学习RAC之前如果需要遍历一个数组的基本操作如下(我们这里使用forin就好了):
NSArray *numbers = @[@1,@2,@3,@4];
for (id num in numbers) {
NSLog(@"%@",num);
}
获得numbers数组第一个元素:
id num = numbers[0];
获得numbers数组最后一个元素:
id num = numbers[[numbers count]-1];
这是利用系统实现的方式,对于RAC设计一切皆转换为信号,根据这样的思路,我们可以考虑将数组转换成一个信号,然后去订阅这个数组的内容就可以了。
NSArray *numbers = @[@1,@2,@3,@4];
[numbers.rac_sequence.signal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
事实上,过程正如我们所想的那样,RAC的确将数组转换成了信号。
获得numbers数组第一个元素:
RACSequence *seq = numbers.rac_sequence;
NSLog(@"%@",seq.head);
获得numbers数组最后一个元素:
NSLog(@"%@",seq.tail);
RAC 数组操作分析
我们看过如何使用后,开始继续源码分析。
数组转信号 rac_sequence
我们跳入rac_sequence来分析,首先下面这个方法是在数组分类中的方法。
#import “NSArray+RACSequenceAdditions.h”
#import “RACArraySequence.h”
@implementation NSArray (RACSequenceAdditions)
@end
- (RACSequence *)rac_sequence {
return [RACArraySequence sequenceWithArray:self offset:0];
}
调用了RACArraySequence将数组转换为RAC自己定义的集合。
+ (instancetype)sequenceWithArray:(NSArray *)array offset:(NSUInteger)offset {
NSCParameterAssert(offset <= array.count);
if (offset == array.count) return self.empty;
RACArraySequence *seq = [[self alloc] init];
seq->_backingArray = [array copy];
seq->_offset = offset;
return seq;
}
首先查看传入的偏移量,不然发现,在数组转换的时候,默认的偏离值为0,如果当数组大小为0时候,则说明传入的数组大小为空。
RACArraySequence作用: 从给定的偏移量开始,返回一个用于枚举给定数组的序列。该阵列将被复制以防止突变。
这里面保存传入的数组和一个偏移量大小。
转换为一个可以枚举的数组序列后,该将此序列转换为一个信号。
- (RACSignal *)signal {
return [[self signalWithScheduler:[RACScheduler scheduler]] setNameWithFormat:@"[%@] -signal", self.name];
}
- (RACSignal *)signalWithScheduler:(RACScheduler *)scheduler {
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
__block RACSequence *sequence = self;
return [scheduler scheduleRecursiveBlock:^(void (^reschedule)(void)) {
if (sequence.head == nil) {
[subscriber sendCompleted];
return;
}
/* 订阅者发送信号 */
[subscriber sendNext:sequence.head];
sequence = sequence.tail;
reschedule();
}];
}] setNameWithFormat:@"[%@] -signalWithScheduler:", self.name];
}
这一部分核心内容其实就只有两行。通过第一句可以看见转换成为信号后就已经发送了信息。
[subscriber sendNext:sequence.head];
sequence = sequence.tail;
发送前,我们要查看之前保存的sequence是否有效。如果squence的头为空,则说明此序列为空,因此发送完成。
if (sequence.head == nil) {
[subscriber sendCompleted];
return;
}
在这里我们可以看到sequence.head引领了一整个序列。
RAC数组操作订阅信号
这一部分和前面设计的一样,体现了面向对象的好处有木有。
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL);
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
return [self subscribe:o];
}
至此,我们就可以遍历整个数组的元素,数组中每一项都当作了消息发送出去,然后被订阅。
RAC 字典操作分析
NSDictionary *dict = @{@"name":@"CreaterOS",@"age":@20};
[dict.rac_sequence.signal subscribeNext:^(id x) {
RACTupleUnpack(NSString *key,NSString *value) = x;
NSLog(@"%@ %@",key,value);
}];
如果用系统方法获得字典内容就很棘手了,当然是数据量过多时候,获得就显得不那么轻松,最起码key就够开发者捣鼓一会了。
根据RAC处理发现这个操作和数组是不是极其相似,都是先将数组转换成信号然后订阅。不同点是,字典是一个一个的键值对,因此,取出key和value显得十分重要。不过,RAC已经设计了一个很厉害的宏RACTupleUnpack,用于将元组解包。
RACTupleUnpack 将字典传入作为一个一个元组看待,然后利用解包操作,获得对应元组中的key和value。
/// Declares new object variables and unpacks a RACTuple into them.
///
/// This macro should be used on the left side of an assignment, with the
/// tuple on the right side. Nothing else should appear on the same line, and the
/// macro should not be the only statement in a conditional or loop body.
///
/// If the tuple has more values than there are variables listed, the excess
/// values are ignored.
///
/// If the tuple has fewer values than there are variables listed, the excess
/// variables are initialized to nil.
///
/// Examples
///
/// RACTupleUnpack(NSString *string, NSNumber *num) = [RACTuple tupleWithObjects:@"foo", @5, nil];
/// NSLog(@"string: %@", string);
/// NSLog(@"num: %@", num);
///
/// /* The above is equivalent to: */
/// RACTuple *t = [RACTuple tupleWithObjects:@"foo", @5, nil];
/// NSString *string = t[0];
/// NSNumber *num = t[1];
/// NSLog(@"string: %@", string);
/// NSLog(@"num: %@", num);
#define RACTupleUnpack(...) \
RACTupleUnpack_(__VA_ARGS__)
#define RACTupleUnpack_(...) \
metamacro_foreach(RACTupleUnpack_decl,, __VA_ARGS__) \
\
int RACTupleUnpack_state = 0; \
\
RACTupleUnpack_after: \
; \
metamacro_foreach(RACTupleUnpack_assign,, __VA_ARGS__) \
if (RACTupleUnpack_state != 0) RACTupleUnpack_state = 2; \
\
while (RACTupleUnpack_state != 2) \
if (RACTupleUnpack_state == 1) { \
goto RACTupleUnpack_after; \
} else \
for (; RACTupleUnpack_state != 1; RACTupleUnpack_state = 1) \
[RACTupleUnpackingTrampoline trampoline][ @[ metamacro_foreach(RACTupleUnpack_value,, __VA_ARGS__) ] ]
实话说,这一部分宏定义,小编还没有看懂,因此,在这里就贴上源码供大家学习参考。
为何说这个宏很厉害 当你使用时候会发现,这个宏定义可以提供输入提示,这个设计真的相当亲切。而且,可以自动将key和value拿来使用,这个设计真的厉害
至此,RAC字典操作分析就到此为止了,如果有条件,小编理解了这个宏定义后,会专门写一篇关于这个宏定义设计的文章。
源码下载
中文源码:点击我,前往GitHub下载.