RACDynamicSequence.m
5.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
//
// RACDynamicSequence.m
// ReactiveCocoa
//
// Created by Justin Spahr-Summers on 2012-10-29.
// Copyright (c) 2012 GitHub. All rights reserved.
//
#import "RACDynamicSequence.h"
#import <libkern/OSAtomic.h>
// Determines how RACDynamicSequences will be deallocated before the next one is
// shifted onto the autorelease pool.
//
// This avoids stack overflows when deallocating long chains of dynamic
// sequences.
#define DEALLOC_OVERFLOW_GUARD 100
@interface RACDynamicSequence () {
// The value for the "head" property, if it's been evaluated already.
//
// Because it's legal for head to be nil, this ivar is valid any time
// headBlock is nil.
//
// This ivar should only be accessed while synchronized on self.
id _head;
// The value for the "tail" property, if it's been evaluated already.
//
// Because it's legal for tail to be nil, this ivar is valid any time
// tailBlock is nil.
//
// This ivar should only be accessed while synchronized on self.
RACSequence *_tail;
// The result of an evaluated `dependencyBlock`.
//
// This ivar is valid any time `hasDependency` is YES and `dependencyBlock`
// is nil.
//
// This ivar should only be accessed while synchronized on self.
id _dependency;
}
// A block used to evaluate head. This should be set to nil after `_head` has been
// initialized.
//
// This is marked `strong` instead of `copy` because of some bizarre block
// copying bug. See https://github.com/ReactiveCocoa/ReactiveCocoa/pull/506.
//
// The signature of this block varies based on the value of `hasDependency`:
//
// - If YES, this block is of type `id (^)(id)`.
// - If NO, this block is of type `id (^)(void)`.
//
// This property should only be accessed while synchronized on self.
@property (nonatomic, strong) id headBlock;
// A block used to evaluate tail. This should be set to nil after `_tail` has been
// initialized.
//
// This is marked `strong` instead of `copy` because of some bizarre block
// copying bug. See https://github.com/ReactiveCocoa/ReactiveCocoa/pull/506.
//
// The signature of this block varies based on the value of `hasDependency`:
//
// - If YES, this block is of type `RACSequence * (^)(id)`.
// - If NO, this block is of type `RACSequence * (^)(void)`.
//
// This property should only be accessed while synchronized on self.
@property (nonatomic, strong) id tailBlock;
// Whether the receiver was initialized with a `dependencyBlock`.
//
// This property should only be accessed while synchronized on self.
@property (nonatomic, assign) BOOL hasDependency;
// A dependency which must be evaluated before `headBlock` and `tailBlock`. This
// should be set to nil after `_dependency` and `dependencyBlockExecuted` have
// been set.
//
// This is marked `strong` instead of `copy` because of some bizarre block
// copying bug. See https://github.com/ReactiveCocoa/ReactiveCocoa/pull/506.
//
// This property should only be accessed while synchronized on self.
@property (nonatomic, strong) id (^dependencyBlock)(void);
@end
@implementation RACDynamicSequence
#pragma mark Lifecycle
+ (RACSequence *)sequenceWithHeadBlock:(id (^)(void))headBlock tailBlock:(RACSequence *(^)(void))tailBlock {
NSCParameterAssert(headBlock != nil);
RACDynamicSequence *seq = [[RACDynamicSequence alloc] init];
seq.headBlock = [headBlock copy];
seq.tailBlock = [tailBlock copy];
seq.hasDependency = NO;
return seq;
}
+ (RACSequence *)sequenceWithLazyDependency:(id (^)(void))dependencyBlock headBlock:(id (^)(id dependency))headBlock tailBlock:(RACSequence *(^)(id dependency))tailBlock {
NSCParameterAssert(dependencyBlock != nil);
NSCParameterAssert(headBlock != nil);
RACDynamicSequence *seq = [[RACDynamicSequence alloc] init];
seq.headBlock = [headBlock copy];
seq.tailBlock = [tailBlock copy];
seq.dependencyBlock = [dependencyBlock copy];
seq.hasDependency = YES;
return seq;
}
- (void)dealloc {
static volatile int32_t directDeallocCount = 0;
if (OSAtomicIncrement32(&directDeallocCount) >= DEALLOC_OVERFLOW_GUARD) {
OSAtomicAdd32(-DEALLOC_OVERFLOW_GUARD, &directDeallocCount);
// Put this sequence's tail onto the autorelease pool so we stop
// recursing.
__autoreleasing RACSequence *tail __attribute__((unused)) = _tail;
}
_tail = nil;
}
#pragma mark RACSequence
- (id)head {
@synchronized (self) {
id untypedHeadBlock = self.headBlock;
if (untypedHeadBlock == nil) return _head;
if (self.hasDependency) {
if (self.dependencyBlock != nil) {
_dependency = self.dependencyBlock();
self.dependencyBlock = nil;
}
id (^headBlock)(id) = untypedHeadBlock;
_head = headBlock(_dependency);
} else {
id (^headBlock)(void) = untypedHeadBlock;
_head = headBlock();
}
self.headBlock = nil;
return _head;
}
}
- (RACSequence *)tail {
@synchronized (self) {
id untypedTailBlock = self.tailBlock;
if (untypedTailBlock == nil) return _tail;
if (self.hasDependency) {
if (self.dependencyBlock != nil) {
_dependency = self.dependencyBlock();
self.dependencyBlock = nil;
}
RACSequence * (^tailBlock)(id) = untypedTailBlock;
_tail = tailBlock(_dependency);
} else {
RACSequence * (^tailBlock)(void) = untypedTailBlock;
_tail = tailBlock();
}
if (_tail.name == nil) _tail.name = self.name;
self.tailBlock = nil;
return _tail;
}
}
#pragma mark NSObject
- (NSString *)description {
id head = @"(unresolved)";
id tail = @"(unresolved)";
@synchronized (self) {
if (self.headBlock == nil) head = _head;
if (self.tailBlock == nil) {
tail = _tail;
if (tail == self) tail = @"(self)";
}
}
return [NSString stringWithFormat:@"<%@: %p>{ name = %@, head = %@, tail = %@ }", self.class, self, self.name, head, tail];
}
@end