RACDynamicSequence.m 5.6 KB

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