RLMObservation.hpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2015 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #import <Foundation/Foundation.h>
  19. #import <realm/obj.hpp>
  20. #import <realm/object-store/binding_context.hpp>
  21. #import <realm/table.hpp>
  22. @class RLMObjectBase, RLMRealm, RLMSchema, RLMProperty, RLMObjectSchema;
  23. class RLMClassInfo;
  24. class RLMSchemaInfo;
  25. namespace realm {
  26. class History;
  27. class SharedGroup;
  28. }
  29. // RLMObservationInfo stores all of the KVO-related data for RLMObjectBase and
  30. // RLMArray. There is a one-to-one relationship between observed objects and
  31. // RLMObservationInfo instances, so it could be folded into RLMObjectBase, and
  32. // is a separate class mostly to avoid making all accessor objects far larger.
  33. //
  34. // RLMClassInfo stores a vector of pointers to the first observation info
  35. // created for each row. If there are multiple observation infos for a single
  36. // row (such as if there are multiple observed objects backed by a single row,
  37. // or if both an object and an array property of that object are observed),
  38. // they're stored in an intrusive doubly-linked-list in the `next` and `prev`
  39. // members. This is done primarily to make it simpler and faster to loop over
  40. // all of the observed objects for a single row, as that needs to be done for
  41. // every change.
  42. class RLMObservationInfo {
  43. public:
  44. RLMObservationInfo(id object);
  45. RLMObservationInfo(RLMClassInfo &objectSchema, realm::ObjKey row, id object);
  46. ~RLMObservationInfo();
  47. realm::Obj const& getRow() const {
  48. return row;
  49. }
  50. NSString *columnName(realm::ColKey col) const noexcept;
  51. // Send willChange/didChange notifications to all observers for this object/row
  52. // Sends the array versions if indexes is non-nil, normal versions otherwise
  53. void willChange(NSString *key, NSKeyValueChange kind=NSKeyValueChangeSetting, NSIndexSet *indexes=nil) const;
  54. void didChange(NSString *key, NSKeyValueChange kind=NSKeyValueChangeSetting, NSIndexSet *indexes=nil) const;
  55. bool isForRow(realm::ObjKey key) const {
  56. return row.get_key() == key;
  57. }
  58. void recordObserver(realm::Obj& row, RLMClassInfo *objectInfo, RLMObjectSchema *objectSchema, NSString *keyPath);
  59. void removeObserver();
  60. bool hasObservers() const { return observerCount > 0; }
  61. // valueForKey: on observed object and array properties needs to return the
  62. // same object each time for KVO to work at all. Doing this all the time
  63. // requires some odd semantics to avoid reference cycles, so instead we do
  64. // it only to the extent specifically required by KVO. In addition, we
  65. // need to continue to return the same object even if this row is deleted,
  66. // or deleting an object with active observers will explode horribly.
  67. // Once prepareForInvalidation() is called, valueForKey() will always return
  68. // the cached value for object and array properties without checking the
  69. // backing row to verify it's up-to-date.
  70. //
  71. // prepareForInvalidation() must be called on the head of the linked list
  72. // (i.e. on the object pointed to directly by the object schema)
  73. id valueForKey(NSString *key);
  74. void prepareForInvalidation();
  75. private:
  76. // Doubly-linked-list of observed objects for the same row as this
  77. RLMObservationInfo *next = nullptr;
  78. RLMObservationInfo *prev = nullptr;
  79. // Row being observed
  80. realm::Obj row;
  81. RLMClassInfo *objectSchema = nullptr;
  82. // Object doing the observing
  83. __unsafe_unretained id object = nil;
  84. // valueForKey: hack
  85. bool invalidated = false;
  86. size_t observerCount = 0;
  87. NSString *lastKey = nil;
  88. __unsafe_unretained RLMProperty *lastProp = nil;
  89. // objects returned from valueForKey() to keep them alive in case observers
  90. // are added and so that they can still be accessed after row is detached
  91. NSMutableDictionary *cachedObjects;
  92. void setRow(realm::Table const& table, realm::ObjKey newRow);
  93. template<typename F>
  94. void forEach(F&& f) const {
  95. // The user's observation handler may release their last reference to
  96. // the object being observed, which will result in the RLMObservationInfo
  97. // being destroyed. As a result, we need to retain the object which owns
  98. // both `this` and the current info we're looking at.
  99. __attribute__((objc_precise_lifetime)) id self = object, current;
  100. for (auto info = prev; info; info = info->prev) {
  101. current = info->object;
  102. f(info->object);
  103. }
  104. for (auto info = this; info; info = info->next) {
  105. current = info->object;
  106. f(info->object);
  107. }
  108. }
  109. // Default move/copy constructors don't work due to the intrusive linked
  110. // list and we don't need them
  111. RLMObservationInfo(RLMObservationInfo const&) = delete;
  112. RLMObservationInfo(RLMObservationInfo&&) = delete;
  113. RLMObservationInfo& operator=(RLMObservationInfo const&) = delete;
  114. RLMObservationInfo& operator=(RLMObservationInfo&&) = delete;
  115. };
  116. // Get the the observation info chain for the given row
  117. // Will simply return info if it's non-null, and will search ojectSchema's array
  118. // for a matching one otherwise, and return null if there are none
  119. RLMObservationInfo *RLMGetObservationInfo(RLMObservationInfo *info, realm::ObjKey row, RLMClassInfo& objectSchema);
  120. // delete all objects from a single table with change notifications
  121. void RLMClearTable(RLMClassInfo &realm);
  122. class RLMObservationTracker {
  123. public:
  124. RLMObservationTracker(RLMRealm *realm, bool trackDeletions=false);
  125. ~RLMObservationTracker();
  126. void trackDeletions();
  127. void willChange(RLMObservationInfo *info, NSString *key,
  128. NSKeyValueChange kind=NSKeyValueChangeSetting,
  129. NSIndexSet *indexes=nil);
  130. void didChange();
  131. private:
  132. std::vector<std::vector<RLMObservationInfo *> *> _observedTables;
  133. __unsafe_unretained RLMRealm const*_realm;
  134. realm::Group& _group;
  135. RLMObservationInfo *_info = nullptr;
  136. NSString *_key;
  137. NSKeyValueChange _kind = NSKeyValueChangeSetting;
  138. NSIndexSet *_indexes;
  139. struct Change {
  140. RLMObservationInfo *info;
  141. __unsafe_unretained NSString *property;
  142. NSMutableIndexSet *indexes;
  143. };
  144. std::vector<Change> _changes;
  145. std::vector<RLMObservationInfo *> _invalidated;
  146. template<typename CascadeNotification>
  147. void cascadeNotification(CascadeNotification const&);
  148. };
  149. std::vector<realm::BindingContext::ObserverState> RLMGetObservedRows(RLMSchemaInfo const& schema);
  150. void RLMWillChange(std::vector<realm::BindingContext::ObserverState> const& observed, std::vector<void *> const& invalidated);
  151. void RLMDidChange(std::vector<realm::BindingContext::ObserverState> const& observed, std::vector<void *> const& invalidated);