RACTuple分析
了解过swift
的人应该知道元祖,而RACTuple
就相当于元祖。
下面分析中用到的所有测试用例在这里。
首先看下.h
中的文件。
#define RACTuplePack(...) \RACTuplePack_(__VA_ARGS__)#define RACTuplePack_(...) \
([RACTuple tupleWithObjectsFromArray:@[ metamacro_foreach(RACTuplePack_object_or_ractuplenil,, __VA_ARGS__) ]])#define RACTuplePack_object_or_ractuplenil(INDEX, ARG) \
(ARG) ?: RACTupleNil.tupleNil,
可知道RACTuplePack
通过调用RACTuple
的tupleWithObjectsFromArray:
方法生成一个元祖对象。
测试用例:
- (void)testRACTuplePack
{RACTuple *tuple = RACTuplePack(@(1), @(2));NSLog(@"RACTuplePack -- %@", tuple);// 打印日志如下:/*2018-08-12 17:00:21.548262+0800 TestRACTuple[46495:9889422] RACTuplePack -- <RACTuple: 0x600000012ce0> (1,2)*/
}
#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__) ] ]
可知,RACTupleUnpack
通过RACTupleUnpackingTrampoline
类获取元祖内的值。
测试用例:
- (void)testRACTupleUnpack
{RACTuple *tuple = RACTuplePack(@(1), @(2));RACTupleUnpack(NSNumber *number1, NSNumber *number2) = tuple;NSLog(@"RACTupleUnpack -- %@ -- %@", number1, number2);// 打印日志如下:/*2018-08-12 17:03:03.919722+0800 TestRACTuple[46664:9898579] RACTupleUnpack -- 1 -- 2*/
}
接下来是RACTupleNil
类,提供了一个tupleNil
方法,查看.m
中的实现:
+ (RACTupleNil *)tupleNil {static dispatch_once_t onceToken;static RACTupleNil *tupleNil = nil;dispatch_once(&onceToken, ^{tupleNil = [[self alloc] init];});return tupleNil;
}#pragma mark NSCopying- (id)copyWithZone:(NSZone *)zone {return self;
}#pragma mark NSCoding- (id)initWithCoder:(NSCoder *)coder {// Always return the singleton.return self.class.tupleNil;
}- (void)encodeWithCoder:(NSCoder *)coder {
}
RACTupleNil
是个单例类,代表着一个空的对象,方便代替空值存到元祖中。
测试用例:
- (void)test_tupleNil
{RACTupleNil *nil1 = [RACTupleNil tupleNil];RACTupleNil *nil2 = [RACTupleNil tupleNil];NSLog(@"tupleNil -- %@ -- %@", nil1, nil2);// 打印日志如下:/*2018-08-12 17:05:14.905132+0800 TestRACTuple[46777:9905544] tupleNil -- <RACTupleNil: 0x600000207540> -- <RACTupleNil: 0x600000207540>*/
}
接下来就是RACTuple
类了,
first
元祖中的第一个值。second
元祖中的第二个值。third
元祖中的第三个值。fourth
元祖中的第四个值。fifth
元祖中的第五个值。last
元祖中的最后一个值。+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array;
通过array
初始化一个元祖对象。+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array convertNullsToNils:(BOOL)convert;
通过array
convert
初始化一个元祖对象。+ (instancetype)tupleWithObjects:(id)object, ... NS_REQUIRES_NIL_TERMINATION;
通过一系列object
初始化元祖对象。- (id)objectAtIndex:(NSUInteger)index;
获取元祖中指定索引index
下的对象。注意,这里不同于NSArray
,如果index
越界,不会造成崩溃,而是返回nil
值。- (NSArray *)allObjects;
以数组的形式获取元祖的所有对象。- (instancetype)tupleByAddingObject:(id)obj;
向元祖中增加对象。
接下来看看.m
中方法的实现。
@interface RACTuple ()
@property (nonatomic, strong) NSArray *backingArray;
@end
RACTuple
中有个数组类型的实例变量backingArray
。其实元祖中的值就是保存在这个数组当中的。
- (instancetype)init {self = [super init];if (self == nil) return nil;self.backingArray = [NSArray array];return self;
}
初始化方法, 同时初始化backingArray
数组。
- (NSString *)description {return [NSString stringWithFormat:@"<%@: %p> %@", self.class, self, self.allObjects];
}
格式化打印日志。
- (BOOL)isEqual:(RACTuple *)object {if (object == self) return YES;if (![object isKindOfClass:self.class]) return NO;return [self.backingArray isEqual:object.backingArray];
}
重写isEqual
方法用于定义元祖相等的原则,元祖对象的指针相等或者元祖对象的数组通过isEqual:
方法相等。
测试用例如下:
- (void)test_equal
{RACTuple *tuple1 = [RACTuple tupleWithObjects:@(1), @(2), nil];RACTuple *tuple2 = tuple1;RACTuple *tuple3 = [RACTuple tupleWithObjects:@(1), @(2), nil];RACTuple *tuple4 = [RACTuple tupleWithObjects:@(1), @(3), nil];NSLog(@"equal -- %@ -- %@ -- %@ -- %@", tuple1, tuple2, tuple3, tuple4);NSLog(@"equal -- %d -- %d -- %d", [tuple1 isEqual:tuple2], [tuple1 isEqual:tuple3], [tuple1 isEqual:tuple4]);// 打印日志如下:/*2018-08-12 17:10:21.677911+0800 TestRACTuple[47013:9921207] equal -- <RACTuple: 0x604000005540> (1,2) -- <RACTuple: 0x604000005540> (1,2) -- <RACTuple: 0x604000005560> (1,2) -- <RACTuple: 0x604000005580> (1,3)2018-08-12 17:10:21.678304+0800 TestRACTuple[47013:9921207] equal -- 1 -- 1 -- 0*/
}
- (NSUInteger)hash {return self.backingArray.hash;
}
元祖对象的hash
值就是backingArray
的hash
值。
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len {return [self.backingArray countByEnumeratingWithState:state objects:buffer count:len];
}
快速枚举协议方法,通过backingArray
完成该协议方法的实现。
测试用例:
- (void)test_Enumerating
{RACTuple *tuple = [RACTuple tupleWithObjects:@(1), @(2), @(3), nil];for (NSNumber *number in tuple) {NSLog(@"Enumerating -- %@", number);}// 打印日志如下:/*2018-08-12 17:46:12.879880+0800 TestRACTuple[47403:9946784] Enumerating -- 12018-08-12 17:46:12.880064+0800 TestRACTuple[47403:9946784] Enumerating -- 22018-08-12 17:46:12.880165+0800 TestRACTuple[47403:9946784] Enumerating -- 3*/
}
- (instancetype)copyWithZone:(NSZone *)zone {// we're immutable, bitches!return self;
}
通过此方法可知元祖不支持拷贝。
测试用例:
- (void)test_copy
{RACTuple *tuple = [RACTuple tupleWithObjects:@(1), @(2), nil];RACTuple *tuple1 = [tuple copy];NSLog(@"copy - %@ - %@", tuple, tuple1);// 打印日志如下:/*2018-08-12 17:49:38.201583+0800 TestRACTuple[47592:9957830] copy - <RACTuple: 0x604000003310> (1,2) - <RACTuple: 0x604000003310> (1,2)*/
}
- (id)initWithCoder:(NSCoder *)coder {self = [self init];if (self == nil) return nil;self.backingArray = [coder decodeObjectForKey:@keypath(self.backingArray)];return self;
}- (void)encodeWithCoder:(NSCoder *)coder {if (self.backingArray != nil) [coder encodeObject:self.backingArray forKey:@keypath(self.backingArray)];
}
序列化方法,主要是对backingArray
的序列化。
测试用例:
- (void)test_code
{RACTuple *tuple = [RACTuple tupleWithObjects:@(1), nil];NSData *data = [NSKeyedArchiver archivedDataWithRootObject:tuple];if (data) {RACTuple *tuple1 = [NSKeyedUnarchiver unarchiveObjectWithData:data];NSLog(@"code -- %@ -- %@", tuple, tuple1);}// 测试用例:/*2018-08-12 17:54:46.112251+0800 TestRACTuple[47844:9973666] code -- <RACTuple: 0x60400001a450> (1) -- <RACTuple: 0x60400001a480> (1)*/
}
+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array {return [self tupleWithObjectsFromArray:array convertNullsToNils:NO];
}+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array convertNullsToNils:(BOOL)convert {RACTuple *tuple = [[self alloc] init];if (convert) {NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count];for (id object in array) {[newArray addObject:(object == NSNull.null ? RACTupleNil.tupleNil : object)];}tuple.backingArray = newArray;} else {tuple.backingArray = [array copy];}return tuple;
}
类初始化方法,将array
中的数据放到元祖对象的backingArray
中。通过convert
决定是否将数组中的NSNull
对象转换成RACTupleNil
对象。
测试用例:
- (void)test_tupleWithObjectsFromArray
{NSArray *array = @[@(1), NSNull.null, @(2)];RACTuple *tuple1 = [RACTuple tupleWithObjectsFromArray:array];RACTuple *tuple2 = [RACTuple tupleWithObjectsFromArray:array convertNullsToNils:YES];NSLog(@"tupleWithObjectsFromArray -- %@ -- %@", [tuple1 objectAtIndex:1], [tuple2 objectAtIndex:1]);// 打印日志:/*2018-08-12 18:01:08.918020+0800 TestRACTuple[48181:9994069] tupleWithObjectsFromArray -- <null> -- (null)*/
}
+ (instancetype)tupleWithObjects:(id)object, ... {RACTuple *tuple = [[self alloc] init];va_list args;va_start(args, object);NSUInteger count = 0;for (id currentObject = object; currentObject != nil; currentObject = va_arg(args, id)) {++count;}va_end(args);if (count == 0) {tuple.backingArray = @[];return tuple;}NSMutableArray *objects = [[NSMutableArray alloc] initWithCapacity:count];va_start(args, object);for (id currentObject = object; currentObject != nil; currentObject = va_arg(args, id)) {[objects addObject:currentObject];}va_end(args);tuple.backingArray = objects;return tuple;
}
通过对可变参数的循环获取到所有的参数对象,并保存到backingArray
中,完成初始化操作。注意,可变参数是以nil
结束的。类似于NSArray
的方法。
测试用例:
- (void)test_tupleWithObjects
{RACTuple *tuple1 = [RACTuple tupleWithObjects:@(1), nil, @(2), nil];RACTuple *tuple2 = [RACTuple tupleWithObjects:@(1), [RACTupleNil tupleNil], @(2), nil];NSLog(@"tupleWithObjects -- %@ -- %@", tuple1, tuple2);// 打印日志:/*2018-08-12 18:04:18.575558+0800 TestRACTuple[48333:10003693] tupleWithObjects -- <RACTuple: 0x600000007660> (1) -- <RACTuple: 0x600000007690> (1,"<null>",2)*/
}
- (id)objectAtIndex:(NSUInteger)index {if (index >= self.count) return nil;id object = self.backingArray[index];return (object == RACTupleNil.tupleNil ? nil : object);
}
获取backingArray
中索引index
对应的值,里面针对索引做了判断,防止越界。如果对象是RACTupleNil.tupleNil
返回nil
。
测试用例:
- (void)test_objectAtIndex
{RACTuple *tuple = [RACTuple tupleWithObjects:@(1), @(2), nil];NSLog(@"objectAtIndex -- %@ -- %@ -- %@", [tuple objectAtIndex:-1], [tuple objectAtIndex:0], [tuple objectAtIndex:2]);// 打印日志:/*2018-08-12 18:08:13.772026+0800 TestRACTuple[48531:10016043] objectAtIndex -- (null) -- 1 -- (null)*/
}
- (NSArray *)allObjects {NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:self.backingArray.count];for (id object in self.backingArray) {[newArray addObject:(object == RACTupleNil.tupleNil ? NSNull.null : object)];}return newArray;
}
通过对backingArray
的遍历获取到所有的值,并保存到数组中返回,注意如果数组中的对象是RACTupleNil.tupleNil
,则将使用NSNull.null
代替,还原出原始值。
测试用例:
- (void)test_allObjects
{RACTuple *tuple = [RACTuple tupleWithObjects:@(1), [NSNull null], @(2), nil];NSLog(@"allObjects -- %@", [tuple allObjects]);// 打印日志:/*2018-08-12 18:09:57.500308+0800 TestRACTuple[48627:10021917] allObjects -- (1,"<null>",2)*/
}
- (instancetype)tupleByAddingObject:(id)obj {NSArray *newArray = [self.backingArray arrayByAddingObject:obj ?: RACTupleNil.tupleNil];return [self.class tupleWithObjectsFromArray:newArray];
}
将obj
添加到backingArray
数组中,同时进行了空值判断,防止崩溃。
测试用例:
- (void)test_tupleByAddingObject
{RACTuple *tuple = [RACTuple tupleWithObjects:@(1), @(2), nil];RACTuple *tuple1 = [tuple tupleByAddingObject:@"3"];NSLog(@"tupleByAddingObject -- %@", tuple1);RACTuple *tuple2 = [tuple tupleByAddingObject:nil];NSLog(@"tupleByAddingObject -- %@", tuple2);// 打印日志:/*2018-08-12 18:13:16.836348+0800 TestRACTuple[50529:10137576] tupleByAddingObject -- <RACTuple: 0x600000015240> (1,2,3)2018-08-12 18:13:16.837194+0800 TestRACTuple[50529:10137576] tupleByAddingObject -- <RACTuple: 0x600000015260> (1,2,"<null>")*/
}
- (NSUInteger)count {return self.backingArray.count;
}
返回backingArray
的count
值。
测试用例:
- (void)test_count
{RACTuple *tuple = [RACTuple tupleWithObjects:@(1), @"2", nil];NSLog(@"count -- %ld", tuple.count);// 打印日志:/*2018-08-12 18:14:48.930810+0800 TestRACTuple[48889:10037610] count -- 2*/
}
- (id)first {return self[0];
}- (id)second {return self[1];
}- (id)third {return self[2];
}- (id)fourth {return self[3];
}- (id)fifth {return self[4];
}- (id)last {return self[self.count - 1];
}
通过语法糖,返回指定索引下的值。这里为什么可以这么使用呢?
因为元祖实现了objectAtIndexedSubscript:
方法,便可以使用上面的语法糖。
- (id)objectAtIndexedSubscript:(NSUInteger)idx {return [self objectAtIndex:idx];
}
通过调用objectAtIndex:
获取到指定索引下的对象。
测试用例:
- (void)test_value
{RACTuple *tuple = [RACTuple tupleWithObjects:@(1), @(2), @(3), @(4), @5, @6, @7, @8, @9, nil];NSLog(@"value -- %@ -- %@ -- %@ -- %@ -- %@ -- %@", tuple.first, tuple.second, tuple.third, tuple.fourth, tuple.fifth, tuple.last);// 打印日志:/*2018-08-12 18:17:45.352995+0800 TestRACTuple[49055:10047385] value -- 1 -- 2 -- 3 -- 4 -- 5 -- 9*/
}
接着就是RACTuple (RACSequenceAdditions)
类目中的方法,
- (RACSequence *)rac_sequence {return [RACTupleSequence sequenceWithTupleBackingArray:self.backingArray offset:0];
}
通过backingArray
生成一个RACTupleSequence
对象。如果对RACTupleSequence
还不了解,请看这篇文章。
测试用例:
- (void)test_rac_sequence
{RACTuple *tuple = [RACTuple tupleWithObjects:@(1), @(2), nil];NSLog(@"rac_sequence -- %@", [tuple rac_sequence]);// 打印日志:/*2018-08-12 18:19:28.986037+0800 TestRACTuple[49157:10053178] rac_sequence -- <RACTupleSequence: 0x6040002399c0>{ name = , tuple = (1,2) }*/
}
RACTuple (ObjectSubscripting)
中的方法objectAtIndexedSubscript:
上面已经分析过了。
接下来看RACTupleUnpackingTrampoline
。
+ (instancetype)trampoline {static dispatch_once_t onceToken;static id trampoline = nil;dispatch_once(&onceToken, ^{trampoline = [[self alloc] init];});return trampoline;
}
通过类方法初始化一个单例对象。
- (void)setObject:(RACTuple *)tuple forKeyedSubscript:(NSArray *)variables {NSCParameterAssert(variables != nil);[variables enumerateObjectsUsingBlock:^(NSValue *value, NSUInteger index, BOOL *stop) {__strong id *ptr = (__strong id *)value.pointerValue;*ptr = tuple[index];}];
}
通过对variables
循环遍历,将tuple
中的值保存到variables
数组中的对象value
的 pointerValue
上。注意,实现该方法的话同样可以像使用NSDictionary
一样,使用语法糖对该对象进行key-value
赋值。之前说过RACTupleUnpack
中就是使用了这个方法,如下:
#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__) ] ]
注意最后的[RACTupleUnpackingTrampoline trampoline][ @[ metamacro_foreach(RACTupleUnpack_value,, __VA_ARGS__) ] ]
刚好是以数组作为key
值的,也就是上面方法中的variables
,如果此时将一个元祖对象赋值过去,那么@[ metamacro_foreach(RACTupleUnpack_value,, __VA_ARGS__)
这个数组刚好就可以拿到元祖中所有的值了。
测试用例:
- (void)test_trampoline
{RACTuple *tuple = [RACTuple tupleWithObjects:@(1), @(2), @"3", nil];RACTupleUnpackingTrampoline *trampoline = [RACTupleUnpackingTrampoline trampoline];NSNumber *number1;NSNumber *number2;NSString *string;NSArray *array = @[[NSValue valueWithPointer:&number1], [NSValue valueWithPointer:&number2], [NSValue valueWithPointer:&string]];trampoline[array] = tuple;NSLog(@"trampoline -- %@ -- %@ -- %@", number1, number2, string);// 打印日志:/*2018-08-12 19:20:36.395290+0800 TestRACTuple[50192:10116319] trampoline -- 1 -- 2 -- 3*/
}
上面就是有关RACTuple
的所有方法,其实作用跟NSArray
一样,只不过做了一些额外的操作防止操作过程中出现崩溃现象。
RACTuple分析相关推荐
- ②、iOS-RAC-核心类分析-RACPassthroughSubscriber订阅者-RACScheduler调度者-RACDisposable销毁者-RACObseve监听者-RACSubject
iOS RAC系列 ①.iOS-RAC的开发用法-底层分析以及总结 ②.iOS-RAC-核心类分析-RACPassthroughSubscriber订阅者-RACScheduler调度者-RACDis ...
- ①、iOS-RAC的开发用法-底层分析以及总结
iOS RAC系列 ①.iOS-RAC的开发用法-底层分析以及总结 ②.iOS-RAC-核心类分析-RACPassthroughSubscriber订阅者-RACScheduler调度者-RACDis ...
- 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析
目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...
- 2022-2028年中国自动驾驶系统行业现状调研分析报告
[报告类型]产业研究 [报告价格]4500起 [出版时间]即时更新(交付时间约3个工作日) [发布机构]智研瞻产业研究院 [报告格式]PDF版 本报告介绍了中国自动驾驶系统行业市场行业相关概述.中国自 ...
- 2022-2028年中国阻尼涂料市场研究及前瞻分析报告
[报告类型]产业研究 [报告价格]4500起 [出版时间]即时更新(交付时间约3个工作日) [发布机构]智研瞻产业研究院 [报告格式]PDF版 本报告介绍了中国阻尼涂料行业市场行业相关概述.中国阻尼涂 ...
- 2021-2028年中国阻燃装饰行业市场需求与投资规划分析报告
[报告类型]产业研究 [报告价格]4500起 [出版时间]即时更新(交付时间约3个工作日) [发布机构]智研瞻产业研究院 [报告格式]PDF版 本报告介绍了中国阻燃装饰行业市场行业相关概述.中国阻燃装 ...
- 2022-2028年全球与中国漂白吸水棉市场研究及前瞻分析报告
[报告类型]产业研究 [报告价格]4500起 [出版时间]即时更新(交付时间约3个工作日) [发布机构]智研瞻产业研究院 [报告格式]PDF版 本报告介绍了全球与中国漂白吸水棉行业市场行业相关概述.全 ...
- 2022-2028年全球与中国青苔清洗剂市场研究及前瞻分析报告
[报告类型]产业研究 [报告价格]4500起 [出版时间]即时更新(交付时间约3个工作日) [发布机构]智研瞻产业研究院 [报告格式]PDF版 本报告介绍了全球与中国青苔清洗剂行业市场行业相关概述.全 ...
- 2022-2028年全球与中国氢碘化物市场智研瞻分析报告
[报告类型]产业研究 [报告价格]4500起 [出版时间]即时更新(交付时间约3个工作日) [发布机构]智研瞻产业研究院 [报告格式]PDF版 本报告介绍了全球与中国氢碘化物行业市场行业相关概述.全球 ...
最新文章
- 科研指导:深度学习的应用研究课程
- RabbitMQ安装遇到的问题及解决记录
- 微信小程序 时间操作
- wxWidgets:wxQueryLayoutInfoEvent类用法
- php 常用的系统函数
- 新项目jenkis配置
- python限制输入长度_textFiled限制输入长度.
- iptables表与链的相关性图
- 12个新鲜出炉的Web开发框架
- jquery 文档就绪
- plsqldev 技巧
- 时间序列分析:非平稳序列的确定性分析
- Wordpress网站渗透测试(进阶详细思路)
- Origin2018-小白安装
- 课程设计---宾馆客房管理系统
- 我的世界服务器自动西瓜,我的世界自动化红石教程 全自动西瓜农场
- 统计学中p值计算公式_统计学中的P值如何计算?
- 视频编码中的I帧、P帧、B帧的概念和特点
- html页面字体飞入飞出特效,JS网页特效:星空飞入效果
- 第二集 第一魂环 第十一章
热门文章
- 四、SpringMVC文件上传
- 计算机无法登录到网络,电脑无法连接到这个网络是什么原因
- Android开发中内存、内部存储、外部存储详解
- Halcon 之 Measure_Pairs
- S32K系列之ADC
- 自媒体初学者如何正确学习视频剪辑【视频制作自学成大神】?
- Rust 能否替代 C 语言,主宰 Linux 的世界?
- The Tao of Programming
- 网站后台没有提示声怎么办_收藏 | 没有 PS 怎么办?10个在线作图网站,轻松搞定图片设计...
- cmos管宽长比,OC, OD门和线与逻辑,传输门,竞争冒险,三态门