ReactiveCocoa源码精讲(四) -- 集合操作

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下载.

发布了5 篇原创文章 · 获赞 0 · 访问量 33

猜你喜欢

转载自blog.csdn.net/weixin_39647415/article/details/105626489
今日推荐