RLMManagedSet.mm 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2020 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 "RLMSet_Private.hpp"
  19. #import "RLMAccessor.hpp"
  20. #import "RLMObjectSchema_Private.hpp"
  21. #import "RLMObjectStore.h"
  22. #import "RLMObject_Private.hpp"
  23. #import "RLMObservation.hpp"
  24. #import "RLMProperty_Private.h"
  25. #import "RLMQueryUtil.hpp"
  26. #import "RLMRealm_Private.hpp"
  27. #import "RLMSchema.h"
  28. #import "RLMSectionedResults_Private.hpp"
  29. #import "RLMThreadSafeReference_Private.hpp"
  30. #import "RLMUtil.hpp"
  31. #import <realm/collection.hpp>
  32. #import <realm/object-store/set.hpp>
  33. #import <realm/set.hpp>
  34. #import <realm/object-store/results.hpp>
  35. #import <realm/object-store/shared_realm.hpp>
  36. @interface RLMManagedSetHandoverMetadata : NSObject
  37. @property (nonatomic) NSString *parentClassName;
  38. @property (nonatomic) NSString *key;
  39. @end
  40. @implementation RLMManagedSetHandoverMetadata
  41. @end
  42. @interface RLMManagedSet () <RLMThreadConfined_Private>
  43. @end
  44. //
  45. // RLMSet implementation
  46. //
  47. @implementation RLMManagedSet {
  48. @public
  49. realm::object_store::Set _backingSet;
  50. RLMRealm *_realm;
  51. RLMClassInfo *_objectInfo;
  52. RLMClassInfo *_ownerInfo;
  53. std::unique_ptr<RLMObservationInfo> _observationInfo;
  54. }
  55. - (RLMManagedSet *)initWithBackingCollection:(realm::object_store::Set)set
  56. parentInfo:(RLMClassInfo *)parentInfo
  57. property:(__unsafe_unretained RLMProperty *const)property {
  58. if (property.type == RLMPropertyTypeObject)
  59. self = [self initWithObjectClassName:property.objectClassName];
  60. else
  61. self = [self initWithObjectType:property.type
  62. optional:property.optional];
  63. if (self) {
  64. _realm = parentInfo->realm;
  65. REALM_ASSERT(set.get_realm() == _realm->_realm);
  66. _backingSet = std::move(set);
  67. _ownerInfo = parentInfo;
  68. if (property.type == RLMPropertyTypeObject)
  69. _objectInfo = &parentInfo->linkTargetType(property.index);
  70. else
  71. _objectInfo = _ownerInfo;
  72. _key = property.name;
  73. }
  74. return self;
  75. }
  76. - (RLMManagedSet *)initWithParent:(__unsafe_unretained RLMObjectBase *const)parentObject
  77. property:(__unsafe_unretained RLMProperty *const)property {
  78. __unsafe_unretained RLMRealm *const realm = parentObject->_realm;
  79. auto col = parentObject->_info->tableColumn(property);
  80. return [self initWithBackingCollection:realm::object_store::Set(realm->_realm, parentObject->_row, col)
  81. parentInfo:parentObject->_info
  82. property:property];
  83. }
  84. - (RLMManagedSet *)initWithParent:(realm::Obj)parent
  85. property:(__unsafe_unretained RLMProperty *const)property
  86. parentInfo:(RLMClassInfo&)info {
  87. auto col = info.tableColumn(property);
  88. return [self initWithBackingCollection:realm::object_store::Set(info.realm->_realm, parent, col)
  89. parentInfo:&info
  90. property:property];
  91. }
  92. void RLMValidateSetObservationKey(__unsafe_unretained NSString *const keyPath,
  93. __unsafe_unretained RLMSet *const set) {
  94. if (![keyPath isEqualToString:RLMInvalidatedKey]) {
  95. @throw RLMException(@"[<%@ %p> addObserver:forKeyPath:options:context:] is not supported. Key path: %@",
  96. [set class], set, keyPath);
  97. }
  98. }
  99. void RLMEnsureSetObservationInfo(std::unique_ptr<RLMObservationInfo>& info,
  100. __unsafe_unretained NSString *const keyPath,
  101. __unsafe_unretained RLMSet *const set,
  102. __unsafe_unretained id const observed) {
  103. RLMValidateSetObservationKey(keyPath, set);
  104. if (!info && set.class == [RLMManagedSet class]) {
  105. auto lv = static_cast<RLMManagedSet *>(set);
  106. info = std::make_unique<RLMObservationInfo>(*lv->_ownerInfo,
  107. lv->_backingSet.get_parent_object_key(),
  108. observed);
  109. }
  110. }
  111. template<typename Function>
  112. __attribute__((always_inline))
  113. static auto translateErrors(Function&& f) {
  114. return translateCollectionError(static_cast<Function&&>(f), @"Set");
  115. }
  116. static void changeSet(__unsafe_unretained RLMManagedSet *const set,
  117. dispatch_block_t f) {
  118. translateErrors([&] { set->_backingSet.verify_in_transaction(); });
  119. RLMObservationTracker tracker(set->_realm, false);
  120. tracker.trackDeletions();
  121. auto obsInfo = RLMGetObservationInfo(set->_observationInfo.get(),
  122. set->_backingSet.get_parent_object_key(),
  123. *set->_ownerInfo);
  124. if (obsInfo) {
  125. tracker.willChange(obsInfo, set->_key);
  126. }
  127. translateErrors(f);
  128. }
  129. //
  130. // public method implementations
  131. //
  132. - (RLMRealm *)realm {
  133. return _realm;
  134. }
  135. - (NSUInteger)count {
  136. return translateErrors([&] { return _backingSet.size(); });
  137. }
  138. - (NSArray<id> *)allObjects {
  139. NSMutableArray *arr = [NSMutableArray new];
  140. for (id prop : self) {
  141. [arr addObject:prop];
  142. }
  143. return arr;
  144. }
  145. - (BOOL)isInvalidated {
  146. return translateErrors([&] { return !_backingSet.is_valid(); });
  147. }
  148. - (RLMClassInfo *)objectInfo {
  149. return _objectInfo;
  150. }
  151. - (bool)isBackedBySet:(realm::object_store::Set const&)set {
  152. return _backingSet == set;
  153. }
  154. - (BOOL)isEqual:(id)object {
  155. return [object respondsToSelector:@selector(isBackedBySet:)] && [object isBackedBySet:_backingSet];
  156. }
  157. - (NSUInteger)hash {
  158. return std::hash<realm::object_store::Set>()(_backingSet);
  159. }
  160. - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
  161. objects:(__unused __unsafe_unretained id [])buffer
  162. count:(NSUInteger)len {
  163. return RLMFastEnumerate(state, len, self);
  164. }
  165. static void RLMInsertObject(RLMManagedSet *set, id object) {
  166. RLMSetValidateMatchingObjectType(set, object);
  167. changeSet(set, ^{
  168. RLMAccessorContext context(*set->_objectInfo);
  169. set->_backingSet.insert(context, object);
  170. });
  171. }
  172. static void RLMRemoveObject(RLMManagedSet *set, id object) {
  173. RLMSetValidateMatchingObjectType(set, object);
  174. changeSet(set, ^{
  175. RLMAccessorContext context(*set->_objectInfo);
  176. set->_backingSet.remove(context, object);
  177. });
  178. }
  179. static void ensureInWriteTransaction(NSString *message, RLMManagedSet *set, RLMManagedSet *otherSet) {
  180. if (!set.realm.inWriteTransaction && !otherSet.realm.inWriteTransaction) {
  181. @throw RLMException(@"Can only perform %@ in a Realm in a write transaction - call beginWriteTransaction on an RLMRealm instance first.", message);
  182. }
  183. }
  184. - (void)addObject:(id)object {
  185. RLMInsertObject(self, object);
  186. }
  187. - (void)addObjects:(id<NSFastEnumeration>)objects {
  188. changeSet(self, ^{
  189. RLMAccessorContext context(*_objectInfo);
  190. for (id obj in objects) {
  191. RLMSetValidateMatchingObjectType(self, obj);
  192. _backingSet.insert(context, obj);
  193. }
  194. });
  195. }
  196. - (void)removeObject:(id)object {
  197. RLMRemoveObject(self, object);
  198. }
  199. - (void)removeAllObjects {
  200. changeSet(self, ^{
  201. _backingSet.remove_all();
  202. });
  203. }
  204. - (void)replaceAllObjectsWithObjects:(NSArray *)objects {
  205. changeSet(self, ^{
  206. RLMAccessorContext context(*_objectInfo);
  207. _backingSet.assign(context, objects);
  208. });
  209. }
  210. - (RLMManagedSet *)managedObjectFrom:(RLMSet *)set {
  211. auto managedSet = RLMDynamicCast<RLMManagedSet>(set);
  212. if (!managedSet) {
  213. @throw RLMException(@"Right hand side value must be a managed Set.");
  214. }
  215. if (_type != managedSet->_type) {
  216. @throw RLMException(@"Cannot intersect sets of type '%@' and '%@'.",
  217. RLMTypeToString(_type), RLMTypeToString(managedSet->_type));
  218. }
  219. if (_realm != managedSet->_realm) {
  220. @throw RLMException(@"Cannot insersect sets managed by different Realms.");
  221. }
  222. if (_objectInfo != managedSet->_objectInfo) {
  223. @throw RLMException(@"Cannot intersect sets of type '%@' and '%@'.",
  224. _objectInfo->rlmObjectSchema.className,
  225. managedSet->_objectInfo->rlmObjectSchema.className);
  226. }
  227. return managedSet;
  228. }
  229. - (BOOL)isSubsetOfSet:(RLMSet<id> *)set {
  230. RLMManagedSet *rhs = [self managedObjectFrom:set];
  231. return _backingSet.is_subset_of(rhs->_backingSet);
  232. }
  233. - (BOOL)intersectsSet:(RLMSet<id> *)set {
  234. RLMManagedSet *rhs = [self managedObjectFrom:set];
  235. return _backingSet.intersects(rhs->_backingSet);
  236. }
  237. - (BOOL)containsObject:(id)obj {
  238. RLMSetValidateMatchingObjectType(self, obj);
  239. RLMAccessorContext context(*_objectInfo);
  240. auto r = _backingSet.find(context, obj);
  241. return r != realm::npos;
  242. }
  243. - (BOOL)isEqualToSet:(RLMSet<id> *)set {
  244. RLMManagedSet *rhs = [self managedObjectFrom:set];
  245. return [self isEqual:rhs];
  246. }
  247. - (void)setSet:(RLMSet<id> *)set {
  248. RLMManagedSet *rhs = [self managedObjectFrom:set];
  249. ensureInWriteTransaction(@"[RLMSet setSet:]", self, rhs);
  250. changeSet(self, ^{
  251. RLMAccessorContext context(*_objectInfo);
  252. _backingSet.assign(context, rhs);
  253. });
  254. }
  255. - (void)intersectSet:(RLMSet<id> *)set {
  256. RLMManagedSet *rhs = [self managedObjectFrom:set];
  257. ensureInWriteTransaction(@"[RLMSet intersectSet:]", self, rhs);
  258. changeSet(self, ^{
  259. _backingSet.assign_intersection(rhs->_backingSet);
  260. });
  261. }
  262. - (void)unionSet:(RLMSet<id> *)set {
  263. RLMManagedSet *rhs = [self managedObjectFrom:set];
  264. ensureInWriteTransaction(@"[RLMSet unionSet:]", self, rhs);
  265. changeSet(self, ^{
  266. _backingSet.assign_union(rhs->_backingSet);
  267. });
  268. }
  269. - (void)minusSet:(RLMSet<id> *)set {
  270. RLMManagedSet *rhs = [self managedObjectFrom:set];
  271. ensureInWriteTransaction(@"[RLMSet minusSet:]", self, rhs);
  272. changeSet(self, ^{
  273. _backingSet.assign_difference(rhs->_backingSet);
  274. });
  275. }
  276. - (id)objectAtIndex:(NSUInteger)index {
  277. return translateErrors([&] {
  278. RLMAccessorContext context(*_objectInfo);
  279. return _backingSet.get(context, index);
  280. });
  281. }
  282. - (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes {
  283. size_t count = self.count;
  284. NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:indexes.count];
  285. RLMAccessorContext context(*_objectInfo);
  286. for (NSUInteger i = indexes.firstIndex; i != NSNotFound; i = [indexes indexGreaterThanIndex:i]) {
  287. if (i >= count) {
  288. return nil;
  289. }
  290. [result addObject:_backingSet.get(context, i)];
  291. }
  292. return result;
  293. }
  294. - (id)firstObject {
  295. return translateErrors([&] {
  296. RLMAccessorContext context(*_objectInfo);
  297. return _backingSet.size() ? _backingSet.get(context, 0) : nil;
  298. });
  299. }
  300. - (id)lastObject {
  301. return translateErrors([&] {
  302. RLMAccessorContext context(*_objectInfo);
  303. size_t size = _backingSet.size();
  304. return size ? _backingSet.get(context, size - 1) : nil;
  305. });
  306. }
  307. - (id)valueForKeyPath:(NSString *)keyPath {
  308. if ([keyPath hasPrefix:@"@"]) {
  309. // Delegate KVC collection operators to RLMResults
  310. return translateErrors([&] {
  311. auto results = [RLMResults resultsWithObjectInfo:*_objectInfo results:_backingSet.as_results()];
  312. return [results valueForKeyPath:keyPath];
  313. });
  314. }
  315. return [super valueForKeyPath:keyPath];
  316. }
  317. - (id)valueForKey:(NSString *)key {
  318. // Ideally we'd use "@invalidated" for this so that "invalidated" would use
  319. // normal array KVC semantics, but observing @things works very oddly (when
  320. // it's part of a key path, it's triggered automatically when array index
  321. // changes occur, and can't be sent explicitly, but works normally when it's
  322. // the entire key path), and an RLMManagedSet *can't* have objects where
  323. // invalidated is true, so we're not losing much.
  324. return translateErrors([&]() -> id {
  325. if ([key isEqualToString:RLMInvalidatedKey]) {
  326. return @(!_backingSet.is_valid());
  327. }
  328. _backingSet.verify_attached();
  329. return [NSSet setWithArray:RLMCollectionValueForKey(_backingSet, key, *_objectInfo)];
  330. });
  331. return nil;
  332. }
  333. - (void)setValue:(id)value forKey:(NSString *)key {
  334. if ([key isEqualToString:@"self"]) {
  335. RLMSetValidateMatchingObjectType(self, value);
  336. RLMAccessorContext context(*_objectInfo);
  337. translateErrors([&] {
  338. _backingSet.remove_all();
  339. _backingSet.insert(context, value);
  340. return;
  341. });
  342. } else if (_type == RLMPropertyTypeObject) {
  343. RLMSetValidateMatchingObjectType(self, value);
  344. translateErrors([&] { _backingSet.verify_in_transaction(); });
  345. RLMCollectionSetValueForKey(self, key, value);
  346. }
  347. else {
  348. [self setValue:value forUndefinedKey:key];
  349. }
  350. }
  351. - (id)minOfProperty:(NSString *)property {
  352. auto column = columnForProperty(property, _backingSet, _objectInfo, _type, RLMCollectionTypeSet);
  353. auto value = translateErrors([&] { return _backingSet.min(column); });
  354. return value ? RLMMixedToObjc(*value) : nil;
  355. }
  356. - (id)maxOfProperty:(NSString *)property {
  357. auto column = columnForProperty(property, _backingSet, _objectInfo, _type, RLMCollectionTypeSet);
  358. auto value = translateErrors([&] { return _backingSet.max(column); });
  359. return value ? RLMMixedToObjc(*value) : nil;
  360. }
  361. - (id)sumOfProperty:(NSString *)property {
  362. auto column = columnForProperty(property, _backingSet, _objectInfo, _type, RLMCollectionTypeSet);
  363. return RLMMixedToObjc(translateErrors([&] { return _backingSet.sum(column); }));
  364. }
  365. - (id)averageOfProperty:(NSString *)property {
  366. auto column = columnForProperty(property, _backingSet, _objectInfo, _type, RLMCollectionTypeSet);
  367. auto value = translateErrors([&] { return _backingSet.average(column); });
  368. return value ? RLMMixedToObjc(*value) : nil;
  369. }
  370. - (void)deleteObjectsFromRealm {
  371. if (_type != RLMPropertyTypeObject) {
  372. @throw RLMException(@"Cannot delete objects from RLMSet<%@>: only RLMObjects can be deleted.", RLMTypeToString(_type));
  373. }
  374. // delete all target rows from the realm
  375. RLMObservationTracker tracker(_realm, true);
  376. translateErrors([&] { _backingSet.delete_all(); });
  377. }
  378. - (RLMResults *)sortedResultsUsingDescriptors:(NSArray<RLMSortDescriptor *> *)properties {
  379. return translateErrors([&] {
  380. return [RLMResults resultsWithObjectInfo:*_objectInfo
  381. results:_backingSet.sort(RLMSortDescriptorsToKeypathArray(properties))];
  382. });
  383. }
  384. - (RLMResults *)distinctResultsUsingKeyPaths:(NSArray<NSString *> *)keyPaths {
  385. return translateErrors([&] {
  386. auto results = [RLMResults resultsWithObjectInfo:*_objectInfo results:_backingSet.as_results()];
  387. return [results distinctResultsUsingKeyPaths:keyPaths];
  388. });
  389. }
  390. - (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
  391. if (_type != RLMPropertyTypeObject) {
  392. @throw RLMException(@"Querying is currently only implemented for sets of Realm Objects");
  393. }
  394. auto query = RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema, _realm.schema, _realm.group);
  395. auto results = translateErrors([&] { return _backingSet.filter(std::move(query)); });
  396. return [RLMResults resultsWithObjectInfo:*_objectInfo results:std::move(results)];
  397. }
  398. - (RLMSectionedResults *)sectionedResultsSortedUsingKeyPath:(NSString *)keyPath
  399. ascending:(BOOL)ascending
  400. keyBlock:(RLMSectionedResultsKeyBlock)keyBlock {
  401. return [[RLMSectionedResults alloc] initWithResults:[self sortedResultsUsingKeyPath:keyPath ascending:ascending]
  402. keyBlock:keyBlock];
  403. }
  404. - (RLMSectionedResults *)sectionedResultsUsingSortDescriptors:(NSArray<RLMSortDescriptor *> *)sortDescriptors
  405. keyBlock:(RLMSectionedResultsKeyBlock)keyBlock {
  406. return [[RLMSectionedResults alloc] initWithResults:[self sortedResultsUsingDescriptors:sortDescriptors]
  407. keyBlock:keyBlock];
  408. }
  409. - (void)addObserver:(id)observer
  410. forKeyPath:(NSString *)keyPath
  411. options:(NSKeyValueObservingOptions)options
  412. context:(void *)context {
  413. RLMEnsureSetObservationInfo(_observationInfo, keyPath, self, self);
  414. [super addObserver:observer forKeyPath:keyPath options:options context:context];
  415. }
  416. - (RLMFastEnumerator *)fastEnumerator {
  417. return translateErrors([&] {
  418. return [[RLMFastEnumerator alloc] initWithBackingCollection:_backingSet
  419. collection:self
  420. classInfo:*_objectInfo];
  421. });
  422. }
  423. - (realm::TableView)tableView {
  424. return translateErrors([&] { return _backingSet.get_query(); }).find_all();
  425. }
  426. - (BOOL)isFrozen {
  427. return _realm.isFrozen;
  428. }
  429. - (instancetype)resolveInRealm:(RLMRealm *)realm {
  430. auto& parentInfo = _ownerInfo->resolve(realm);
  431. return translateErrors([&] {
  432. return [[self.class alloc] initWithBackingCollection:_backingSet.freeze(realm->_realm)
  433. parentInfo:&parentInfo
  434. property:parentInfo.rlmObjectSchema[_key]];
  435. });
  436. }
  437. - (instancetype)freeze {
  438. if (self.frozen) {
  439. return self;
  440. }
  441. return [self resolveInRealm:_realm.freeze];
  442. }
  443. - (instancetype)thaw {
  444. if (!self.frozen) {
  445. return self;
  446. }
  447. return [self resolveInRealm:_realm.thaw];
  448. }
  449. - (realm::NotificationToken)addNotificationCallback:(id)block
  450. keyPaths:(std::optional<std::vector<std::vector<std::pair<realm::TableKey, realm::ColKey>>>>&&)keyPaths {
  451. return _backingSet.add_notification_callback(RLMWrapCollectionChangeCallback(block, self, false), std::move(keyPaths));
  452. }
  453. #pragma mark - Thread Confined Protocol Conformance
  454. - (realm::ThreadSafeReference)makeThreadSafeReference {
  455. return _backingSet;
  456. }
  457. - (RLMManagedSetHandoverMetadata *)objectiveCMetadata {
  458. RLMManagedSetHandoverMetadata *metadata = [[RLMManagedSetHandoverMetadata alloc] init];
  459. metadata.parentClassName = _ownerInfo->rlmObjectSchema.className;
  460. metadata.key = _key;
  461. return metadata;
  462. }
  463. + (instancetype)objectWithThreadSafeReference:(realm::ThreadSafeReference)reference
  464. metadata:(RLMManagedSetHandoverMetadata *)metadata
  465. realm:(RLMRealm *)realm {
  466. auto set = reference.resolve<realm::object_store::Set>(realm->_realm);
  467. if (!set.is_valid()) {
  468. return nil;
  469. }
  470. RLMClassInfo *parentInfo = &realm->_info[metadata.parentClassName];
  471. return [[RLMManagedSet alloc] initWithBackingCollection:std::move(set)
  472. parentInfo:parentInfo
  473. property:parentInfo->rlmObjectSchema[metadata.key]];
  474. }
  475. @end