NSObject+RACPropertySubscribing.h 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. //
  2. // NSObject+RACPropertySubscribing.h
  3. // ReactiveCocoa
  4. //
  5. // Created by Josh Abernathy on 3/2/12.
  6. // Copyright (c) 2012 GitHub, Inc. All rights reserved.
  7. //
  8. #import <Foundation/Foundation.h>
  9. #import "EXTKeyPathCoding.h"
  10. #import "metamacros.h"
  11. /// Creates a signal which observes `KEYPATH` on `TARGET` for changes.
  12. ///
  13. /// In either case, the observation continues until `TARGET` _or self_ is
  14. /// deallocated. If any intermediate object is deallocated instead, it will be
  15. /// assumed to have been set to nil.
  16. ///
  17. /// Make sure to `@strongify(self)` when using this macro within a block! The
  18. /// macro will _always_ reference `self`, which can silently introduce a retain
  19. /// cycle within a block. As a result, you should make sure that `self` is a weak
  20. /// reference (e.g., created by `@weakify` and `@strongify`) before the
  21. /// expression that uses `RACObserve`.
  22. ///
  23. /// Examples
  24. ///
  25. /// // Observes self, and doesn't stop until self is deallocated.
  26. /// RACSignal *selfSignal = RACObserve(self, arrayController.items);
  27. ///
  28. /// // Observes the array controller, and stops when self _or_ the array
  29. /// // controller is deallocated.
  30. /// RACSignal *arrayControllerSignal = RACObserve(self.arrayController, items);
  31. ///
  32. /// // Observes obj.arrayController, and stops when self _or_ the array
  33. /// // controller is deallocated.
  34. /// RACSignal *signal2 = RACObserve(obj.arrayController, items);
  35. ///
  36. /// @weakify(self);
  37. /// RACSignal *signal3 = [anotherSignal flattenMap:^(NSArrayController *arrayController) {
  38. /// // Avoids a retain cycle because of RACObserve implicitly referencing
  39. /// // self.
  40. /// @strongify(self);
  41. /// return RACObserve(arrayController, items);
  42. /// }];
  43. ///
  44. /// Returns a signal which sends the current value of the key path on
  45. /// subscription, then sends the new value every time it changes, and sends
  46. /// completed if self or observer is deallocated.
  47. #define RACObserve(TARGET, KEYPATH) \
  48. ({ \
  49. _Pragma("clang diagnostic push") \
  50. _Pragma("clang diagnostic ignored \"-Wreceiver-is-weak\"") \
  51. __weak id target_ = (TARGET); \
  52. [target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \
  53. _Pragma("clang diagnostic pop") \
  54. })
  55. @class RACDisposable;
  56. @class RACSignal;
  57. @interface NSObject (RACPropertySubscribing)
  58. /// Creates a signal to observe the value at the given key path.
  59. ///
  60. /// The initial value is sent on subscription, the subsequent values are sent
  61. /// from whichever thread the change occured on, even if it doesn't have a valid
  62. /// scheduler.
  63. ///
  64. /// Returns a signal that immediately sends the receiver's current value at the
  65. /// given keypath, then any changes thereafter.
  66. #if OS_OBJECT_HAVE_OBJC_SUPPORT
  67. - (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath observer:(__weak NSObject *)observer;
  68. #else
  69. // Swift builds with OS_OBJECT_HAVE_OBJC_SUPPORT=0 for Playgrounds and LLDB :(
  70. - (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath observer:(NSObject *)observer;
  71. #endif
  72. /// Creates a signal to observe the changes of the given key path.
  73. ///
  74. /// The initial value is sent on subscription if `NSKeyValueObservingOptionInitial` is set.
  75. /// The subsequent values are sent from whichever thread the change occured on,
  76. /// even if it doesn't have a valid scheduler.
  77. ///
  78. /// Returns a signal that sends tuples containing the current value at the key
  79. /// path and the change dictionary for each KVO callback.
  80. #if OS_OBJECT_HAVE_OBJC_SUPPORT
  81. - (RACSignal *)rac_valuesAndChangesForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)observer;
  82. #else
  83. - (RACSignal *)rac_valuesAndChangesForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(NSObject *)observer;
  84. #endif
  85. @end
  86. #define RACAble(...) \
  87. metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
  88. (_RACAbleObject(self, __VA_ARGS__)) \
  89. (_RACAbleObject(__VA_ARGS__))
  90. #define _RACAbleObject(object, property) [object rac_signalForKeyPath:@keypath(object, property) observer:self]
  91. #define RACAbleWithStart(...) \
  92. metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
  93. (_RACAbleWithStartObject(self, __VA_ARGS__)) \
  94. (_RACAbleWithStartObject(__VA_ARGS__))
  95. #define _RACAbleWithStartObject(object, property) [object rac_signalWithStartingValueForKeyPath:@keypath(object, property) observer:self]
  96. @interface NSObject (RACUnavailablePropertySubscribing)
  97. + (RACSignal *)rac_signalFor:(NSObject *)object keyPath:(NSString *)keyPath observer:(NSObject *)observer __attribute__((unavailable("Use -rac_valuesForKeyPath:observer: or RACObserve() instead.")));
  98. + (RACSignal *)rac_signalWithStartingValueFor:(NSObject *)object keyPath:(NSString *)keyPath observer:(NSObject *)observer __attribute__((unavailable("Use -rac_valuesForKeyPath:observer: or RACObserve() instead.")));
  99. + (RACSignal *)rac_signalWithChangesFor:(NSObject *)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(NSObject *)observer __attribute__((unavailable("Use -rac_valuesAndChangesForKeyPath:options:observer: instead.")));
  100. - (RACSignal *)rac_signalForKeyPath:(NSString *)keyPath observer:(NSObject *)observer __attribute__((unavailable("Use -rac_valuesForKeyPath:observer: or RACObserve() instead.")));
  101. - (RACSignal *)rac_signalWithStartingValueForKeyPath:(NSString *)keyPath observer:(NSObject *)observer __attribute__((unavailable("Use -rac_valuesForKeyPath:observer: or RACObserve() instead.")));
  102. - (RACDisposable *)rac_deriveProperty:(NSString *)keyPath from:(RACSignal *)signal __attribute__((unavailable("Use -[RACSignal setKeyPath:onObject:] instead")));
  103. @end