123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552 |
- ////////////////////////////////////////////////////////////////////////////
- //
- // Copyright 2020 Realm Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- //
- ////////////////////////////////////////////////////////////////////////////
- #import "RLMSet_Private.hpp"
- #import "RLMAccessor.hpp"
- #import "RLMObjectSchema_Private.hpp"
- #import "RLMObjectStore.h"
- #import "RLMObject_Private.hpp"
- #import "RLMObservation.hpp"
- #import "RLMProperty_Private.h"
- #import "RLMQueryUtil.hpp"
- #import "RLMRealm_Private.hpp"
- #import "RLMSchema.h"
- #import "RLMSectionedResults_Private.hpp"
- #import "RLMThreadSafeReference_Private.hpp"
- #import "RLMUtil.hpp"
- #import <realm/collection.hpp>
- #import <realm/object-store/set.hpp>
- #import <realm/set.hpp>
- #import <realm/object-store/results.hpp>
- #import <realm/object-store/shared_realm.hpp>
- @interface RLMManagedSetHandoverMetadata : NSObject
- @property (nonatomic) NSString *parentClassName;
- @property (nonatomic) NSString *key;
- @end
- @implementation RLMManagedSetHandoverMetadata
- @end
- @interface RLMManagedSet () <RLMThreadConfined_Private>
- @end
- //
- // RLMSet implementation
- //
- @implementation RLMManagedSet {
- @public
- realm::object_store::Set _backingSet;
- RLMRealm *_realm;
- RLMClassInfo *_objectInfo;
- RLMClassInfo *_ownerInfo;
- std::unique_ptr<RLMObservationInfo> _observationInfo;
- }
- - (RLMManagedSet *)initWithBackingCollection:(realm::object_store::Set)set
- parentInfo:(RLMClassInfo *)parentInfo
- property:(__unsafe_unretained RLMProperty *const)property {
- if (property.type == RLMPropertyTypeObject)
- self = [self initWithObjectClassName:property.objectClassName];
- else
- self = [self initWithObjectType:property.type
- optional:property.optional];
- if (self) {
- _realm = parentInfo->realm;
- REALM_ASSERT(set.get_realm() == _realm->_realm);
- _backingSet = std::move(set);
- _ownerInfo = parentInfo;
- if (property.type == RLMPropertyTypeObject)
- _objectInfo = &parentInfo->linkTargetType(property.index);
- else
- _objectInfo = _ownerInfo;
- _key = property.name;
- }
- return self;
- }
- - (RLMManagedSet *)initWithParent:(__unsafe_unretained RLMObjectBase *const)parentObject
- property:(__unsafe_unretained RLMProperty *const)property {
- __unsafe_unretained RLMRealm *const realm = parentObject->_realm;
- auto col = parentObject->_info->tableColumn(property);
- return [self initWithBackingCollection:realm::object_store::Set(realm->_realm, parentObject->_row, col)
- parentInfo:parentObject->_info
- property:property];
- }
- - (RLMManagedSet *)initWithParent:(realm::Obj)parent
- property:(__unsafe_unretained RLMProperty *const)property
- parentInfo:(RLMClassInfo&)info {
- auto col = info.tableColumn(property);
- return [self initWithBackingCollection:realm::object_store::Set(info.realm->_realm, parent, col)
- parentInfo:&info
- property:property];
- }
- void RLMValidateSetObservationKey(__unsafe_unretained NSString *const keyPath,
- __unsafe_unretained RLMSet *const set) {
- if (![keyPath isEqualToString:RLMInvalidatedKey]) {
- @throw RLMException(@"[<%@ %p> addObserver:forKeyPath:options:context:] is not supported. Key path: %@",
- [set class], set, keyPath);
- }
- }
- void RLMEnsureSetObservationInfo(std::unique_ptr<RLMObservationInfo>& info,
- __unsafe_unretained NSString *const keyPath,
- __unsafe_unretained RLMSet *const set,
- __unsafe_unretained id const observed) {
- RLMValidateSetObservationKey(keyPath, set);
- if (!info && set.class == [RLMManagedSet class]) {
- auto lv = static_cast<RLMManagedSet *>(set);
- info = std::make_unique<RLMObservationInfo>(*lv->_ownerInfo,
- lv->_backingSet.get_parent_object_key(),
- observed);
- }
- }
- template<typename Function>
- __attribute__((always_inline))
- static auto translateErrors(Function&& f) {
- return translateCollectionError(static_cast<Function&&>(f), @"Set");
- }
- static void changeSet(__unsafe_unretained RLMManagedSet *const set,
- dispatch_block_t f) {
- translateErrors([&] { set->_backingSet.verify_in_transaction(); });
- RLMObservationTracker tracker(set->_realm, false);
- tracker.trackDeletions();
- auto obsInfo = RLMGetObservationInfo(set->_observationInfo.get(),
- set->_backingSet.get_parent_object_key(),
- *set->_ownerInfo);
- if (obsInfo) {
- tracker.willChange(obsInfo, set->_key);
- }
- translateErrors(f);
- }
- //
- // public method implementations
- //
- - (RLMRealm *)realm {
- return _realm;
- }
- - (NSUInteger)count {
- return translateErrors([&] { return _backingSet.size(); });
- }
- - (NSArray<id> *)allObjects {
- NSMutableArray *arr = [NSMutableArray new];
- for (id prop : self) {
- [arr addObject:prop];
- }
- return arr;
- }
- - (BOOL)isInvalidated {
- return translateErrors([&] { return !_backingSet.is_valid(); });
- }
- - (RLMClassInfo *)objectInfo {
- return _objectInfo;
- }
- - (bool)isBackedBySet:(realm::object_store::Set const&)set {
- return _backingSet == set;
- }
- - (BOOL)isEqual:(id)object {
- return [object respondsToSelector:@selector(isBackedBySet:)] && [object isBackedBySet:_backingSet];
- }
- - (NSUInteger)hash {
- return std::hash<realm::object_store::Set>()(_backingSet);
- }
- - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
- objects:(__unused __unsafe_unretained id [])buffer
- count:(NSUInteger)len {
- return RLMFastEnumerate(state, len, self);
- }
- static void RLMInsertObject(RLMManagedSet *set, id object) {
- RLMSetValidateMatchingObjectType(set, object);
- changeSet(set, ^{
- RLMAccessorContext context(*set->_objectInfo);
- set->_backingSet.insert(context, object);
- });
- }
- static void RLMRemoveObject(RLMManagedSet *set, id object) {
- RLMSetValidateMatchingObjectType(set, object);
- changeSet(set, ^{
- RLMAccessorContext context(*set->_objectInfo);
- set->_backingSet.remove(context, object);
- });
- }
- static void ensureInWriteTransaction(NSString *message, RLMManagedSet *set, RLMManagedSet *otherSet) {
- if (!set.realm.inWriteTransaction && !otherSet.realm.inWriteTransaction) {
- @throw RLMException(@"Can only perform %@ in a Realm in a write transaction - call beginWriteTransaction on an RLMRealm instance first.", message);
- }
- }
- - (void)addObject:(id)object {
- RLMInsertObject(self, object);
- }
- - (void)addObjects:(id<NSFastEnumeration>)objects {
- changeSet(self, ^{
- RLMAccessorContext context(*_objectInfo);
- for (id obj in objects) {
- RLMSetValidateMatchingObjectType(self, obj);
- _backingSet.insert(context, obj);
- }
- });
- }
- - (void)removeObject:(id)object {
- RLMRemoveObject(self, object);
- }
- - (void)removeAllObjects {
- changeSet(self, ^{
- _backingSet.remove_all();
- });
- }
- - (void)replaceAllObjectsWithObjects:(NSArray *)objects {
- changeSet(self, ^{
- RLMAccessorContext context(*_objectInfo);
- _backingSet.assign(context, objects);
- });
- }
- - (RLMManagedSet *)managedObjectFrom:(RLMSet *)set {
- auto managedSet = RLMDynamicCast<RLMManagedSet>(set);
- if (!managedSet) {
- @throw RLMException(@"Right hand side value must be a managed Set.");
- }
- if (_type != managedSet->_type) {
- @throw RLMException(@"Cannot intersect sets of type '%@' and '%@'.",
- RLMTypeToString(_type), RLMTypeToString(managedSet->_type));
- }
- if (_realm != managedSet->_realm) {
- @throw RLMException(@"Cannot insersect sets managed by different Realms.");
- }
- if (_objectInfo != managedSet->_objectInfo) {
- @throw RLMException(@"Cannot intersect sets of type '%@' and '%@'.",
- _objectInfo->rlmObjectSchema.className,
- managedSet->_objectInfo->rlmObjectSchema.className);
- }
- return managedSet;
- }
- - (BOOL)isSubsetOfSet:(RLMSet<id> *)set {
- RLMManagedSet *rhs = [self managedObjectFrom:set];
- return _backingSet.is_subset_of(rhs->_backingSet);
- }
- - (BOOL)intersectsSet:(RLMSet<id> *)set {
- RLMManagedSet *rhs = [self managedObjectFrom:set];
- return _backingSet.intersects(rhs->_backingSet);
- }
- - (BOOL)containsObject:(id)obj {
- RLMSetValidateMatchingObjectType(self, obj);
- RLMAccessorContext context(*_objectInfo);
- auto r = _backingSet.find(context, obj);
- return r != realm::npos;
- }
- - (BOOL)isEqualToSet:(RLMSet<id> *)set {
- RLMManagedSet *rhs = [self managedObjectFrom:set];
- return [self isEqual:rhs];
- }
- - (void)setSet:(RLMSet<id> *)set {
- RLMManagedSet *rhs = [self managedObjectFrom:set];
- ensureInWriteTransaction(@"[RLMSet setSet:]", self, rhs);
- changeSet(self, ^{
- RLMAccessorContext context(*_objectInfo);
- _backingSet.assign(context, rhs);
- });
- }
- - (void)intersectSet:(RLMSet<id> *)set {
- RLMManagedSet *rhs = [self managedObjectFrom:set];
- ensureInWriteTransaction(@"[RLMSet intersectSet:]", self, rhs);
- changeSet(self, ^{
- _backingSet.assign_intersection(rhs->_backingSet);
- });
- }
- - (void)unionSet:(RLMSet<id> *)set {
- RLMManagedSet *rhs = [self managedObjectFrom:set];
- ensureInWriteTransaction(@"[RLMSet unionSet:]", self, rhs);
- changeSet(self, ^{
- _backingSet.assign_union(rhs->_backingSet);
- });
- }
- - (void)minusSet:(RLMSet<id> *)set {
- RLMManagedSet *rhs = [self managedObjectFrom:set];
- ensureInWriteTransaction(@"[RLMSet minusSet:]", self, rhs);
- changeSet(self, ^{
- _backingSet.assign_difference(rhs->_backingSet);
- });
- }
- - (id)objectAtIndex:(NSUInteger)index {
- return translateErrors([&] {
- RLMAccessorContext context(*_objectInfo);
- return _backingSet.get(context, index);
- });
- }
- - (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes {
- size_t count = self.count;
- NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:indexes.count];
- RLMAccessorContext context(*_objectInfo);
- for (NSUInteger i = indexes.firstIndex; i != NSNotFound; i = [indexes indexGreaterThanIndex:i]) {
- if (i >= count) {
- return nil;
- }
- [result addObject:_backingSet.get(context, i)];
- }
- return result;
- }
- - (id)firstObject {
- return translateErrors([&] {
- RLMAccessorContext context(*_objectInfo);
- return _backingSet.size() ? _backingSet.get(context, 0) : nil;
- });
- }
- - (id)lastObject {
- return translateErrors([&] {
- RLMAccessorContext context(*_objectInfo);
- size_t size = _backingSet.size();
- return size ? _backingSet.get(context, size - 1) : nil;
- });
- }
- - (id)valueForKeyPath:(NSString *)keyPath {
- if ([keyPath hasPrefix:@"@"]) {
- // Delegate KVC collection operators to RLMResults
- return translateErrors([&] {
- auto results = [RLMResults resultsWithObjectInfo:*_objectInfo results:_backingSet.as_results()];
- return [results valueForKeyPath:keyPath];
- });
- }
- return [super valueForKeyPath:keyPath];
- }
- - (id)valueForKey:(NSString *)key {
- // Ideally we'd use "@invalidated" for this so that "invalidated" would use
- // normal array KVC semantics, but observing @things works very oddly (when
- // it's part of a key path, it's triggered automatically when array index
- // changes occur, and can't be sent explicitly, but works normally when it's
- // the entire key path), and an RLMManagedSet *can't* have objects where
- // invalidated is true, so we're not losing much.
- return translateErrors([&]() -> id {
- if ([key isEqualToString:RLMInvalidatedKey]) {
- return @(!_backingSet.is_valid());
- }
- _backingSet.verify_attached();
- return [NSSet setWithArray:RLMCollectionValueForKey(_backingSet, key, *_objectInfo)];
- });
- return nil;
- }
- - (void)setValue:(id)value forKey:(NSString *)key {
- if ([key isEqualToString:@"self"]) {
- RLMSetValidateMatchingObjectType(self, value);
- RLMAccessorContext context(*_objectInfo);
- translateErrors([&] {
- _backingSet.remove_all();
- _backingSet.insert(context, value);
- return;
- });
- } else if (_type == RLMPropertyTypeObject) {
- RLMSetValidateMatchingObjectType(self, value);
- translateErrors([&] { _backingSet.verify_in_transaction(); });
- RLMCollectionSetValueForKey(self, key, value);
- }
- else {
- [self setValue:value forUndefinedKey:key];
- }
- }
- - (id)minOfProperty:(NSString *)property {
- auto column = columnForProperty(property, _backingSet, _objectInfo, _type, RLMCollectionTypeSet);
- auto value = translateErrors([&] { return _backingSet.min(column); });
- return value ? RLMMixedToObjc(*value) : nil;
- }
- - (id)maxOfProperty:(NSString *)property {
- auto column = columnForProperty(property, _backingSet, _objectInfo, _type, RLMCollectionTypeSet);
- auto value = translateErrors([&] { return _backingSet.max(column); });
- return value ? RLMMixedToObjc(*value) : nil;
- }
- - (id)sumOfProperty:(NSString *)property {
- auto column = columnForProperty(property, _backingSet, _objectInfo, _type, RLMCollectionTypeSet);
- return RLMMixedToObjc(translateErrors([&] { return _backingSet.sum(column); }));
- }
- - (id)averageOfProperty:(NSString *)property {
- auto column = columnForProperty(property, _backingSet, _objectInfo, _type, RLMCollectionTypeSet);
- auto value = translateErrors([&] { return _backingSet.average(column); });
- return value ? RLMMixedToObjc(*value) : nil;
- }
- - (void)deleteObjectsFromRealm {
- if (_type != RLMPropertyTypeObject) {
- @throw RLMException(@"Cannot delete objects from RLMSet<%@>: only RLMObjects can be deleted.", RLMTypeToString(_type));
- }
- // delete all target rows from the realm
- RLMObservationTracker tracker(_realm, true);
- translateErrors([&] { _backingSet.delete_all(); });
- }
- - (RLMResults *)sortedResultsUsingDescriptors:(NSArray<RLMSortDescriptor *> *)properties {
- return translateErrors([&] {
- return [RLMResults resultsWithObjectInfo:*_objectInfo
- results:_backingSet.sort(RLMSortDescriptorsToKeypathArray(properties))];
- });
- }
- - (RLMResults *)distinctResultsUsingKeyPaths:(NSArray<NSString *> *)keyPaths {
- return translateErrors([&] {
- auto results = [RLMResults resultsWithObjectInfo:*_objectInfo results:_backingSet.as_results()];
- return [results distinctResultsUsingKeyPaths:keyPaths];
- });
- }
- - (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
- if (_type != RLMPropertyTypeObject) {
- @throw RLMException(@"Querying is currently only implemented for sets of Realm Objects");
- }
- auto query = RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema, _realm.schema, _realm.group);
- auto results = translateErrors([&] { return _backingSet.filter(std::move(query)); });
- return [RLMResults resultsWithObjectInfo:*_objectInfo results:std::move(results)];
- }
- - (RLMSectionedResults *)sectionedResultsSortedUsingKeyPath:(NSString *)keyPath
- ascending:(BOOL)ascending
- keyBlock:(RLMSectionedResultsKeyBlock)keyBlock {
- return [[RLMSectionedResults alloc] initWithResults:[self sortedResultsUsingKeyPath:keyPath ascending:ascending]
- keyBlock:keyBlock];
- }
- - (RLMSectionedResults *)sectionedResultsUsingSortDescriptors:(NSArray<RLMSortDescriptor *> *)sortDescriptors
- keyBlock:(RLMSectionedResultsKeyBlock)keyBlock {
- return [[RLMSectionedResults alloc] initWithResults:[self sortedResultsUsingDescriptors:sortDescriptors]
- keyBlock:keyBlock];
- }
- - (void)addObserver:(id)observer
- forKeyPath:(NSString *)keyPath
- options:(NSKeyValueObservingOptions)options
- context:(void *)context {
- RLMEnsureSetObservationInfo(_observationInfo, keyPath, self, self);
- [super addObserver:observer forKeyPath:keyPath options:options context:context];
- }
- - (RLMFastEnumerator *)fastEnumerator {
- return translateErrors([&] {
- return [[RLMFastEnumerator alloc] initWithBackingCollection:_backingSet
- collection:self
- classInfo:*_objectInfo];
- });
- }
- - (realm::TableView)tableView {
- return translateErrors([&] { return _backingSet.get_query(); }).find_all();
- }
- - (BOOL)isFrozen {
- return _realm.isFrozen;
- }
- - (instancetype)resolveInRealm:(RLMRealm *)realm {
- auto& parentInfo = _ownerInfo->resolve(realm);
- return translateErrors([&] {
- return [[self.class alloc] initWithBackingCollection:_backingSet.freeze(realm->_realm)
- parentInfo:&parentInfo
- property:parentInfo.rlmObjectSchema[_key]];
- });
- }
- - (instancetype)freeze {
- if (self.frozen) {
- return self;
- }
- return [self resolveInRealm:_realm.freeze];
- }
- - (instancetype)thaw {
- if (!self.frozen) {
- return self;
- }
- return [self resolveInRealm:_realm.thaw];
- }
- - (realm::NotificationToken)addNotificationCallback:(id)block
- keyPaths:(std::optional<std::vector<std::vector<std::pair<realm::TableKey, realm::ColKey>>>>&&)keyPaths {
- return _backingSet.add_notification_callback(RLMWrapCollectionChangeCallback(block, self, false), std::move(keyPaths));
- }
- #pragma mark - Thread Confined Protocol Conformance
- - (realm::ThreadSafeReference)makeThreadSafeReference {
- return _backingSet;
- }
- - (RLMManagedSetHandoverMetadata *)objectiveCMetadata {
- RLMManagedSetHandoverMetadata *metadata = [[RLMManagedSetHandoverMetadata alloc] init];
- metadata.parentClassName = _ownerInfo->rlmObjectSchema.className;
- metadata.key = _key;
- return metadata;
- }
- + (instancetype)objectWithThreadSafeReference:(realm::ThreadSafeReference)reference
- metadata:(RLMManagedSetHandoverMetadata *)metadata
- realm:(RLMRealm *)realm {
- auto set = reference.resolve<realm::object_store::Set>(realm->_realm);
- if (!set.is_valid()) {
- return nil;
- }
- RLMClassInfo *parentInfo = &realm->_info[metadata.parentClassName];
- return [[RLMManagedSet alloc] initWithBackingCollection:std::move(set)
- parentInfo:parentInfo
- property:parentInfo->rlmObjectSchema[metadata.key]];
- }
- @end
|