RLMObservation.mm 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  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, realm::ObjKey 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(realm::ColKey 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 const& table, realm::ObjKey key) {
  126. REALM_ASSERT_DEBUG(!row);
  127. REALM_ASSERT_DEBUG(objectSchema);
  128. row = table.get_object(key);
  129. for (auto info : objectSchema->observedObjects) {
  130. if (info->row && info->row.get_key() == key) {
  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::Obj& 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_key());
  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. auto col = row.get_table()->get_column_key(lastProp.name.UTF8String);
  215. if (row.is_null(col)) {
  216. [cachedObjects removeObjectForKey:key];
  217. return nil;
  218. }
  219. RLMObjectBase *value = cachedObjects[key];
  220. if (value && value->_row.get_key() == row.get<realm::ObjKey>(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, realm::ObjKey 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. if (!objectSchema.table()) {
  246. // Orphaned embedded object types are included in the schema but do not
  247. // create a table at all, so we may not have a table here and just
  248. // don't need to do anything
  249. return;
  250. }
  251. for (auto info : objectSchema.observedObjects) {
  252. info->willChange(RLMInvalidatedKey);
  253. }
  254. {
  255. RLMObservationTracker tracker(objectSchema.realm, true);
  256. Results(objectSchema.realm->_realm, objectSchema.table()).clear();
  257. for (auto info : objectSchema.observedObjects) {
  258. info->prepareForInvalidation();
  259. }
  260. }
  261. for (auto info : reverse(objectSchema.observedObjects)) {
  262. info->didChange(RLMInvalidatedKey);
  263. }
  264. objectSchema.observedObjects.clear();
  265. }
  266. RLMObservationTracker::RLMObservationTracker(__unsafe_unretained RLMRealm *const realm, bool trackDeletions)
  267. : _realm(realm)
  268. , _group(realm.group)
  269. {
  270. if (trackDeletions) {
  271. this->trackDeletions();
  272. }
  273. }
  274. RLMObservationTracker::~RLMObservationTracker() {
  275. didChange();
  276. }
  277. void RLMObservationTracker::willChange(RLMObservationInfo *info, NSString *key,
  278. NSKeyValueChange kind, NSIndexSet *indexes) {
  279. _key = key;
  280. _kind = kind;
  281. _indexes = indexes;
  282. _info = info;
  283. if (_info) {
  284. _info->willChange(key, kind, indexes);
  285. }
  286. }
  287. void RLMObservationTracker::trackDeletions() {
  288. if (_group.has_cascade_notification_handler()) {
  289. // We're nested inside another call which will handle any cascaded changes for us
  290. return;
  291. }
  292. for (auto& info : _realm->_info) {
  293. if (!info.second.observedObjects.empty()) {
  294. _observedTables.push_back(&info.second.observedObjects);
  295. }
  296. }
  297. // No need for change tracking if no objects are observed
  298. if (_observedTables.empty()) {
  299. return;
  300. }
  301. _group.set_cascade_notification_handler([=](realm::Group::CascadeNotification const& cs) {
  302. cascadeNotification(cs);
  303. });
  304. }
  305. template<typename CascadeNotification>
  306. void RLMObservationTracker::cascadeNotification(CascadeNotification const& cs) {
  307. if (cs.rows.empty() && cs.links.empty()) {
  308. return;
  309. }
  310. size_t invalidatedCount = _invalidated.size();
  311. size_t changeCount = _changes.size();
  312. auto tableKey = [](RLMObservationInfo *info) {
  313. return info->getRow().get_table()->get_key();
  314. };
  315. std::sort(begin(_observedTables), end(_observedTables),
  316. [=](auto a, auto b) { return tableKey(a->front()) < tableKey(b->front()); });
  317. for (auto const& link : cs.links) {
  318. auto table = std::find_if(_observedTables.begin(), _observedTables.end(), [&](auto table) {
  319. return tableKey(table->front()) == link.origin_table;
  320. });
  321. if (table == _observedTables.end()) {
  322. continue;
  323. }
  324. for (auto observer : **table) {
  325. if (!observer->isForRow(link.origin_key)) {
  326. continue;
  327. }
  328. NSString *name = observer->columnName(link.origin_col_key);
  329. if (observer->getRow().get_table()->get_column_type(link.origin_col_key) != type_LinkList) {
  330. _changes.push_back({observer, name});
  331. continue;
  332. }
  333. auto c = find_if(begin(_changes), end(_changes), [&](auto const& c) {
  334. return c.info == observer && c.property == name;
  335. });
  336. if (c == end(_changes)) {
  337. _changes.push_back({observer, name, [NSMutableIndexSet new]});
  338. c = prev(end(_changes));
  339. }
  340. // We know what row index is being removed from the LinkView,
  341. // but what we actually want is the indexes in the LinkView that
  342. // are going away
  343. auto linkview = observer->getRow().get_linklist(link.origin_col_key);
  344. linkview.find_all(link.old_target_key, [&](size_t index) {
  345. [c->indexes addIndex:index];
  346. });
  347. }
  348. }
  349. if (!cs.rows.empty()) {
  350. using Row = realm::Group::CascadeNotification::row;
  351. auto begin = cs.rows.begin();
  352. for (auto table : _observedTables) {
  353. auto currentTableKey = tableKey(table->front());
  354. if (begin->table_key < currentTableKey) {
  355. // Find the first deleted object in or after this table
  356. begin = std::lower_bound(begin, cs.rows.end(), Row{currentTableKey, realm::ObjKey(0)});
  357. }
  358. if (begin == cs.rows.end()) {
  359. // No more deleted objects
  360. break;
  361. }
  362. if (currentTableKey < begin->table_key) {
  363. // Next deleted object is in a table after this one
  364. continue;
  365. }
  366. // Find the end of the deletions in this table
  367. auto end = std::lower_bound(begin, cs.rows.end(), Row{realm::TableKey(currentTableKey.value + 1), realm::ObjKey(0)});
  368. // Check each observed object to see if it's in the deleted rows
  369. for (auto info : *table) {
  370. if (std::binary_search(begin, end, Row{currentTableKey, info->getRow().get_key()})) {
  371. _invalidated.push_back(info);
  372. }
  373. }
  374. // Advance the begin iterator to the start of the next table
  375. begin = end;
  376. if (begin == cs.rows.end()) {
  377. break;
  378. }
  379. }
  380. }
  381. // The relative order of these loops is very important
  382. for (size_t i = invalidatedCount; i < _invalidated.size(); ++i) {
  383. _invalidated[i]->willChange(RLMInvalidatedKey);
  384. }
  385. for (size_t i = changeCount; i < _changes.size(); ++i) {
  386. auto const& change = _changes[i];
  387. change.info->willChange(change.property, NSKeyValueChangeRemoval, change.indexes);
  388. }
  389. for (size_t i = invalidatedCount; i < _invalidated.size(); ++i) {
  390. _invalidated[i]->prepareForInvalidation();
  391. }
  392. }
  393. void RLMObservationTracker::didChange() {
  394. if (_info) {
  395. _info->didChange(_key, _kind, _indexes);
  396. _info = nullptr;
  397. }
  398. if (_observedTables.empty()) {
  399. return;
  400. }
  401. _group.set_cascade_notification_handler(nullptr);
  402. for (auto const& change : reverse(_changes)) {
  403. change.info->didChange(change.property, NSKeyValueChangeRemoval, change.indexes);
  404. }
  405. for (auto info : reverse(_invalidated)) {
  406. info->didChange(RLMInvalidatedKey);
  407. }
  408. _observedTables.clear();
  409. _changes.clear();
  410. _invalidated.clear();
  411. }
  412. namespace {
  413. template<typename Func>
  414. void forEach(realm::BindingContext::ObserverState const& state, Func&& func) {
  415. for (auto& change : state.changes) {
  416. func(realm::ColKey(change.first), change.second, static_cast<RLMObservationInfo *>(state.info));
  417. }
  418. }
  419. }
  420. std::vector<realm::BindingContext::ObserverState> RLMGetObservedRows(RLMSchemaInfo const& schema) {
  421. std::vector<realm::BindingContext::ObserverState> observers;
  422. for (auto& table : schema) {
  423. for (auto info : table.second.observedObjects) {
  424. auto const& row = info->getRow();
  425. if (!row.is_valid())
  426. continue;
  427. observers.push_back({
  428. row.get_table()->get_key(),
  429. row.get_key().value,
  430. info});
  431. }
  432. }
  433. sort(begin(observers), end(observers));
  434. return observers;
  435. }
  436. static NSKeyValueChange convert(realm::BindingContext::ColumnInfo::Kind kind) {
  437. switch (kind) {
  438. case realm::BindingContext::ColumnInfo::Kind::None:
  439. case realm::BindingContext::ColumnInfo::Kind::SetAll:
  440. return NSKeyValueChangeSetting;
  441. case realm::BindingContext::ColumnInfo::Kind::Set:
  442. return NSKeyValueChangeReplacement;
  443. case realm::BindingContext::ColumnInfo::Kind::Insert:
  444. return NSKeyValueChangeInsertion;
  445. case realm::BindingContext::ColumnInfo::Kind::Remove:
  446. return NSKeyValueChangeRemoval;
  447. }
  448. }
  449. static NSIndexSet *convert(realm::IndexSet const& in, NSMutableIndexSet *out) {
  450. if (in.empty()) {
  451. return nil;
  452. }
  453. [out removeAllIndexes];
  454. for (auto range : in) {
  455. [out addIndexesInRange:{range.first, range.second - range.first}];
  456. }
  457. return out;
  458. }
  459. void RLMWillChange(std::vector<realm::BindingContext::ObserverState> const& observed,
  460. std::vector<void *> const& invalidated) {
  461. for (auto info : invalidated) {
  462. static_cast<RLMObservationInfo *>(info)->willChange(RLMInvalidatedKey);
  463. }
  464. if (!observed.empty()) {
  465. NSMutableIndexSet *indexes = [NSMutableIndexSet new];
  466. for (auto const& o : observed) {
  467. forEach(o, [&](realm::ColKey colKey, auto const& change, RLMObservationInfo *info) {
  468. info->willChange(info->columnName(colKey),
  469. convert(change.kind), convert(change.indices, indexes));
  470. });
  471. }
  472. }
  473. for (auto info : invalidated) {
  474. static_cast<RLMObservationInfo *>(info)->prepareForInvalidation();
  475. }
  476. }
  477. void RLMDidChange(std::vector<realm::BindingContext::ObserverState> const& observed,
  478. std::vector<void *> const& invalidated) {
  479. if (!observed.empty()) {
  480. // Loop in reverse order to avoid O(N^2) behavior in Foundation
  481. NSMutableIndexSet *indexes = [NSMutableIndexSet new];
  482. for (auto const& o : reverse(observed)) {
  483. forEach(o, [&](realm::ColKey col, auto const& change, RLMObservationInfo *info) {
  484. info->didChange(info->columnName(col), convert(change.kind), convert(change.indices, indexes));
  485. });
  486. }
  487. }
  488. for (auto const& info : reverse(invalidated)) {
  489. static_cast<RLMObservationInfo *>(info)->didChange(RLMInvalidatedKey);
  490. }
  491. }