blob: c3e979c0813255e08bd86e638bef4c12f2f5d8fb [file] [log] [blame]
//
// RACTuple.m
// ReactiveCocoa
//
// Created by Josh Abernathy on 4/12/12.
// Copyright (c) 2012 GitHub, Inc. All rights reserved.
//
#import "RACTuple.h"
#import "RACEXTKeyPathCoding.h"
#import "RACTupleSequence.h"
@implementation RACTupleNil
+ (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 {
}
@end
@interface RACTuple ()
@property (nonatomic, strong) NSArray *backingArray;
@end
@implementation RACTuple
- (instancetype)init {
self = [super init];
if (self == nil) return nil;
self.backingArray = [NSArray array];
return self;
}
- (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];
}
- (NSUInteger)hash {
return self.backingArray.hash;
}
#pragma mark NSFastEnumeration
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len {
return [self.backingArray countByEnumeratingWithState:state objects:buffer count:len];
}
#pragma mark NSCopying
- (instancetype)copyWithZone:(NSZone *)zone {
// we're immutable, bitches!
return self;
}
#pragma mark NSCoding
- (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)];
}
#pragma mark API
+ (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;
}
+ (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;
}
- (id)objectAtIndex:(NSUInteger)index {
if (index >= self.count) return nil;
id object = [self.backingArray objectAtIndex:index];
return (object == RACTupleNil.tupleNil ? nil : object);
}
- (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;
}
- (instancetype)tupleByAddingObject:(id)obj {
NSArray *newArray = [self.backingArray arrayByAddingObject:obj ?: RACTupleNil.tupleNil];
return [self.class tupleWithObjectsFromArray:newArray convertNullsToNils:NO];
}
- (NSUInteger)count {
return self.backingArray.count;
}
- (id)first {
return [self objectAtIndex:0];
}
- (id)second {
return [self objectAtIndex:1];
}
- (id)third {
return [self objectAtIndex:2];
}
- (id)fourth {
return [self objectAtIndex:3];
}
- (id)fifth {
return [self objectAtIndex:4];
}
- (id)last {
return [self objectAtIndex:self.count - 1];
}
@end
@implementation RACTuple (RACSequenceAdditions)
- (RACSequence *)rac_sequence {
return [RACTupleSequence sequenceWithTupleBackingArray:self.backingArray offset:0];
}
@end
@implementation RACTuple (ObjectSubscripting)
- (id)objectAtIndexedSubscript:(NSUInteger)idx {
return [self objectAtIndex:idx];
}
@end
@implementation RACTupleUnpackingTrampoline
#pragma mark Lifecycle
+ (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];
}];
}
@end