RLMObservation.mm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  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 "RLMObservation.hpp"
  19. #import "RLMAccessor.h"
  20. #import "RLMArray_Private.hpp"
  21. #import "RLMListBase.h"
  22. #import "RLMObjectSchema_Private.hpp"
  23. #import "RLMObject_Private.hpp"
  24. #import "RLMProperty_Private.h"
  25. #import "RLMRealm_Private.hpp"
  26. #import <realm/group.hpp>
  27. using namespace realm;
  28. namespace {
  29. template<typename Iterator>
  30. struct IteratorPair {
  31. Iterator first;
  32. Iterator second;
  33. };
  34. template<typename Iterator>
  35. Iterator begin(IteratorPair<Iterator> const& p) {
  36. return p.first;
  37. }
  38. template<typename Iterator>
  39. Iterator end(IteratorPair<Iterator> const& p) {
  40. return p.second;
  41. }
  42. template<typename Container>
  43. auto reverse(Container const& c) {
  44. return IteratorPair<typename Container::const_reverse_iterator>{c.rbegin(), c.rend()};
  45. }
  46. }
  47. RLMObservationInfo::RLMObservationInfo(RLMClassInfo &objectSchema, std::size_t row, id object)
  48. : object(object)
  49. , objectSchema(&objectSchema)
  50. {
  51. setRow(*objectSchema.table(), row);
  52. }
  53. RLMObservationInfo::RLMObservationInfo(id object)
  54. : object(object)
  55. {
  56. }
  57. RLMObservationInfo::~RLMObservationInfo() {
  58. if (prev) {
  59. // Not the head of the linked list, so just detach from the list
  60. REALM_ASSERT_DEBUG(prev->next == this);
  61. prev->next = next;
  62. if (next) {
  63. REALM_ASSERT_DEBUG(next->prev == this);
  64. next->prev = prev;
  65. }
  66. }
  67. else if (objectSchema) {
  68. // The head of the list, so remove self from the object schema's array
  69. // of observation info, either replacing self with the next info or
  70. // removing entirely if there is no next
  71. auto end = objectSchema->observedObjects.end();
  72. auto it = find(objectSchema->observedObjects.begin(), end, this);
  73. if (it != end) {
  74. if (next) {
  75. *it = next;
  76. next->prev = nullptr;
  77. }
  78. else {
  79. iter_swap(it, std::prev(end));
  80. objectSchema->observedObjects.pop_back();
  81. }
  82. }
  83. }
  84. // Otherwise the observed object was unmanaged, so nothing to do
  85. #ifdef DEBUG
  86. // ensure that incorrect cleanup fails noisily
  87. object = (__bridge id)(void *)-1;
  88. prev = (RLMObservationInfo *)-1;
  89. next = (RLMObservationInfo *)-1;
  90. #endif
  91. }
  92. NSString *RLMObservationInfo::columnName(size_t col) const noexcept {
  93. return objectSchema->propertyForTableColumn(col).name;
  94. }
  95. void RLMObservationInfo::willChange(NSString *key, NSKeyValueChange kind, NSIndexSet *indexes) const {
  96. if (indexes) {
  97. forEach([=](__unsafe_unretained auto o) {
  98. [o willChange:kind valuesAtIndexes:indexes forKey:key];
  99. });
  100. }
  101. else {
  102. forEach([=](__unsafe_unretained auto o) {
  103. [o willChangeValueForKey:key];
  104. });
  105. }
  106. }
  107. void RLMObservationInfo::didChange(NSString *key, NSKeyValueChange kind, NSIndexSet *indexes) const {
  108. if (indexes) {
  109. forEach([=](__unsafe_unretained auto o) {
  110. [o didChange:kind valuesAtIndexes:indexes forKey:key];
  111. });
  112. }
  113. else {
  114. forEach([=](__unsafe_unretained auto o) {
  115. [o didChangeValueForKey:key];
  116. });
  117. }
  118. }
  119. void RLMObservationInfo::prepareForInvalidation() {
  120. REALM_ASSERT_DEBUG(objectSchema);
  121. REALM_ASSERT_DEBUG(!prev);
  122. for (auto info = this; info; info = info->next)
  123. info->invalidated = true;
  124. }
  125. void RLMObservationInfo::setRow(realm::Table &table, size_t newRow) {
  126. REALM_ASSERT_DEBUG(!row);
  127. REALM_ASSERT_DEBUG(objectSchema);
  128. row = table[newRow];
  129. for (auto info : objectSchema->observedObjects) {
  130. if (info->row && info->row.get_index() == row.get_index()) {
  131. prev = info;
  132. next = info->next;
  133. if (next)
  134. next->prev = this;
  135. info->next = this;
  136. return;
  137. }
  138. }
  139. objectSchema->observedObjects.push_back(this);
  140. }
  141. void RLMObservationInfo::recordObserver(realm::Row& objectRow, RLMClassInfo *objectInfo,
  142. __unsafe_unretained RLMObjectSchema *const objectSchema,
  143. __unsafe_unretained NSString *const keyPath) {
  144. ++observerCount;
  145. if (row) {
  146. return;
  147. }
  148. // add ourselves to the list of observed objects if this is the first time
  149. // an observer is being added to a managed object
  150. if (objectRow) {
  151. this->objectSchema = objectInfo;
  152. setRow(*objectRow.get_table(), objectRow.get_index());
  153. return;
  154. }
  155. // Arrays need a reference to their containing object to avoid having to
  156. // go through the awful proxy object from mutableArrayValueForKey.
  157. // For managed objects we do this when the object is added or created
  158. // (and have to to support notifications from modifying an object which
  159. // was never observed), but for Swift classes (both RealmSwift and
  160. // RLMObject) we can't do it then because we don't know what the parent
  161. // object is.
  162. NSUInteger sep = [keyPath rangeOfString:@"."].location;
  163. NSString *key = sep == NSNotFound ? keyPath : [keyPath substringToIndex:sep];
  164. RLMProperty *prop = objectSchema[key];
  165. if (prop && prop.array) {
  166. id value = valueForKey(key);
  167. RLMArray *array = [value isKindOfClass:[RLMListBase class]] ? [value _rlmArray] : value;
  168. array->_key = key;
  169. array->_parentObject = object;
  170. }
  171. else if (auto swiftIvar = prop.swiftIvar) {
  172. if (auto optional = RLMDynamicCast<RLMOptionalBase>(object_getIvar(object, swiftIvar))) {
  173. RLMInitializeUnmanagedOptional(optional, object, prop);
  174. }
  175. }
  176. }
  177. void RLMObservationInfo::removeObserver() {
  178. --observerCount;
  179. }
  180. id RLMObservationInfo::valueForKey(NSString *key) {
  181. if (invalidated) {
  182. if ([key isEqualToString:RLMInvalidatedKey]) {
  183. return @YES;
  184. }
  185. return cachedObjects[key];
  186. }
  187. if (key != lastKey) {
  188. lastKey = key;
  189. lastProp = objectSchema ? objectSchema->rlmObjectSchema[key] : nil;
  190. }
  191. static auto superValueForKey = reinterpret_cast<id(*)(id, SEL, NSString *)>([NSObject methodForSelector:@selector(valueForKey:)]);
  192. if (!lastProp) {
  193. // Not a managed property, so use NSObject's implementation of valueForKey:
  194. return RLMCoerceToNil(superValueForKey(object, @selector(valueForKey:), key));
  195. }
  196. auto getSuper = [&] {
  197. return row ? RLMDynamicGet(object, lastProp) : RLMCoerceToNil(superValueForKey(object, @selector(valueForKey:), key));
  198. };
  199. // We need to return the same object each time for observing over keypaths
  200. // to work, so we store a cache of them here. We can't just cache them on
  201. // the object as that leads to retain cycles.
  202. if (lastProp.array) {
  203. RLMArray *value = cachedObjects[key];
  204. if (!value) {
  205. value = getSuper();
  206. if (!cachedObjects) {
  207. cachedObjects = [NSMutableDictionary new];
  208. }
  209. cachedObjects[key] = value;
  210. }
  211. return value;
  212. }
  213. if (lastProp.type == RLMPropertyTypeObject) {
  214. size_t col = row.get_column_index(lastProp.name.UTF8String);
  215. if (row.is_null_link(col)) {
  216. [cachedObjects removeObjectForKey:key];
  217. return nil;
  218. }
  219. RLMObjectBase *value = cachedObjects[key];
  220. if (value && value->_row.get_index() == row.get_link(col)) {
  221. return value;
  222. }
  223. value = getSuper();
  224. if (!cachedObjects) {
  225. cachedObjects = [NSMutableDictionary new];
  226. }
  227. cachedObjects[key] = value;
  228. return value;
  229. }
  230. return getSuper();
  231. }
  232. RLMObservationInfo *RLMGetObservationInfo(RLMObservationInfo *info, size_t row,
  233. RLMClassInfo& objectSchema) {
  234. if (info) {
  235. return info;
  236. }
  237. for (RLMObservationInfo *info : objectSchema.observedObjects) {
  238. if (info->isForRow(row)) {
  239. return info;
  240. }
  241. }
  242. return nullptr;
  243. }
  244. void RLMClearTable(RLMClassInfo &objectSchema) {
  245. for (auto info : objectSchema.observedObjects) {
  246. info->willChange(RLMInvalidatedKey);
  247. }
  248. RLMTrackDeletions(objectSchema.realm, ^{
  249. Results(objectSchema.realm->_realm, *objectSchema.table()).clear();
  250. for (auto info : objectSchema.observedObjects) {
  251. info->prepareForInvalidation();
  252. }
  253. });
  254. for (auto info : reverse(objectSchema.observedObjects)) {
  255. info->didChange(RLMInvalidatedKey);
  256. }
  257. objectSchema.observedObjects.clear();
  258. }
  259. void RLMTrackDeletions(__unsafe_unretained RLMRealm *const realm, dispatch_block_t block) {
  260. std::vector<std::vector<RLMObservationInfo *> *> observers;
  261. // Build up an array of observation info arrays which is indexed by table
  262. // index (the object schemata may be in an entirely different order)
  263. for (auto& info : realm->_info) {
  264. if (info.second.observedObjects.empty()) {
  265. continue;
  266. }
  267. size_t ndx = info.second.table()->get_index_in_group();
  268. if (ndx >= observers.size()) {
  269. observers.resize(std::max(observers.size() * 2, ndx + 1));
  270. }
  271. observers[ndx] = &info.second.observedObjects;
  272. }
  273. // No need for change tracking if no objects are observed
  274. if (observers.empty()) {
  275. block();
  276. return;
  277. }
  278. struct change {
  279. RLMObservationInfo *info;
  280. __unsafe_unretained NSString *property;
  281. NSMutableIndexSet *indexes;
  282. };
  283. std::vector<change> changes;
  284. std::vector<RLMObservationInfo *> invalidated;
  285. // This callback is called by core with a list of row deletions and
  286. // resulting link nullifications immediately before things are deleted and nullified
  287. realm.group.set_cascade_notification_handler([&](realm::Group::CascadeNotification const& cs) {
  288. for (auto const& link : cs.links) {
  289. size_t table_ndx = link.origin_table->get_index_in_group();
  290. if (table_ndx >= observers.size() || !observers[table_ndx]) {
  291. // The modified table has no observers
  292. continue;
  293. }
  294. for (auto observer : *observers[table_ndx]) {
  295. if (!observer->isForRow(link.origin_row_ndx)) {
  296. continue;
  297. }
  298. NSString *name = observer->columnName(link.origin_col_ndx);
  299. if (observer->getRow().get_table()->get_column_type(link.origin_col_ndx) != type_LinkList) {
  300. changes.push_back({observer, name});
  301. continue;
  302. }
  303. auto c = find_if(begin(changes), end(changes), [&](auto const& c) {
  304. return c.info == observer && c.property == name;
  305. });
  306. if (c == end(changes)) {
  307. changes.push_back({observer, name, [NSMutableIndexSet new]});
  308. c = prev(end(changes));
  309. }
  310. // We know what row index is being removed from the LinkView,
  311. // but what we actually want is the indexes in the LinkView that
  312. // are going away
  313. auto linkview = observer->getRow().get_linklist(link.origin_col_ndx);
  314. size_t start = 0, index;
  315. while ((index = linkview->find(link.old_target_row_ndx, start)) != realm::not_found) {
  316. [c->indexes addIndex:index];
  317. start = index + 1;
  318. }
  319. }
  320. }
  321. for (auto const& row : cs.rows) {
  322. if (row.table_ndx >= observers.size() || !observers[row.table_ndx]) {
  323. // The modified table has no observers
  324. continue;
  325. }
  326. for (auto observer : *observers[row.table_ndx]) {
  327. if (observer->isForRow(row.row_ndx)) {
  328. invalidated.push_back(observer);
  329. break;
  330. }
  331. }
  332. }
  333. // The relative order of these loops is very important
  334. for (auto info : invalidated) {
  335. info->willChange(RLMInvalidatedKey);
  336. }
  337. for (auto const& change : changes) {
  338. change.info->willChange(change.property, NSKeyValueChangeRemoval, change.indexes);
  339. }
  340. for (auto info : invalidated) {
  341. info->prepareForInvalidation();
  342. }
  343. });
  344. try {
  345. block();
  346. }
  347. catch (...) {
  348. realm.group.set_cascade_notification_handler(nullptr);
  349. throw;
  350. }
  351. for (auto const& change : reverse(changes)) {
  352. change.info->didChange(change.property, NSKeyValueChangeRemoval, change.indexes);
  353. }
  354. for (auto info : reverse(invalidated)) {
  355. info->didChange(RLMInvalidatedKey);
  356. }
  357. realm.group.set_cascade_notification_handler(nullptr);
  358. }
  359. namespace {
  360. template<typename Func>
  361. void forEach(realm::BindingContext::ObserverState const& state, Func&& func) {
  362. for (size_t i = 0, size = state.changes.size(); i < size; ++i) {
  363. if (state.changes[i].kind != realm::BindingContext::ColumnInfo::Kind::None) {
  364. func(i, state.changes[i], static_cast<RLMObservationInfo *>(state.info));
  365. }
  366. }
  367. }
  368. }
  369. std::vector<realm::BindingContext::ObserverState> RLMGetObservedRows(RLMSchemaInfo const& schema) {
  370. std::vector<realm::BindingContext::ObserverState> observers;
  371. for (auto& table : schema) {
  372. for (auto info : table.second.observedObjects) {
  373. auto const& row = info->getRow();
  374. if (!row.is_attached())
  375. continue;
  376. observers.push_back({
  377. row.get_table()->get_index_in_group(),
  378. row.get_index(),
  379. info});
  380. }
  381. }
  382. sort(begin(observers), end(observers));
  383. return observers;
  384. }
  385. static NSKeyValueChange convert(realm::BindingContext::ColumnInfo::Kind kind) {
  386. switch (kind) {
  387. case realm::BindingContext::ColumnInfo::Kind::None:
  388. case realm::BindingContext::ColumnInfo::Kind::SetAll:
  389. return NSKeyValueChangeSetting;
  390. case realm::BindingContext::ColumnInfo::Kind::Set:
  391. return NSKeyValueChangeReplacement;
  392. case realm::BindingContext::ColumnInfo::Kind::Insert:
  393. return NSKeyValueChangeInsertion;
  394. case realm::BindingContext::ColumnInfo::Kind::Remove:
  395. return NSKeyValueChangeRemoval;
  396. }
  397. }
  398. static NSIndexSet *convert(realm::IndexSet const& in, NSMutableIndexSet *out) {
  399. if (in.empty()) {
  400. return nil;
  401. }
  402. [out removeAllIndexes];
  403. for (auto range : in) {
  404. [out addIndexesInRange:{range.first, range.second - range.first}];
  405. }
  406. return out;
  407. }
  408. void RLMWillChange(std::vector<realm::BindingContext::ObserverState> const& observed,
  409. std::vector<void *> const& invalidated) {
  410. for (auto info : invalidated) {
  411. static_cast<RLMObservationInfo *>(info)->willChange(RLMInvalidatedKey);
  412. }
  413. if (!observed.empty()) {
  414. NSMutableIndexSet *indexes = [NSMutableIndexSet new];
  415. for (auto const& o : observed) {
  416. forEach(o, [&](size_t, auto const& change, RLMObservationInfo *info) {
  417. info->willChange(info->columnName(change.initial_column_index),
  418. convert(change.kind), convert(change.indices, indexes));
  419. });
  420. }
  421. }
  422. for (auto info : invalidated) {
  423. static_cast<RLMObservationInfo *>(info)->prepareForInvalidation();
  424. }
  425. }
  426. void RLMDidChange(std::vector<realm::BindingContext::ObserverState> const& observed,
  427. std::vector<void *> const& invalidated) {
  428. if (!observed.empty()) {
  429. // Loop in reverse order to avoid O(N^2) behavior in Foundation
  430. NSMutableIndexSet *indexes = [NSMutableIndexSet new];
  431. for (auto const& o : reverse(observed)) {
  432. forEach(o, [&](size_t i, auto const& change, RLMObservationInfo *info) {
  433. info->didChange(info->columnName(i), convert(change.kind), convert(change.indices, indexes));
  434. });
  435. }
  436. }
  437. for (auto const& info : reverse(invalidated)) {
  438. static_cast<RLMObservationInfo *>(info)->didChange(RLMInvalidatedKey);
  439. }
  440. }