RLMObservation.mm 18 KB

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