RLMManagedArray.mm 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2014 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 "RLMArray_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 "RLMThreadSafeReference_Private.hpp"
  29. #import "RLMUtil.hpp"
  30. #import "list.hpp"
  31. #import "results.hpp"
  32. #import "shared_realm.hpp"
  33. #import <realm/table_view.hpp>
  34. #import <objc/runtime.h>
  35. @interface RLMManagedArrayHandoverMetadata : NSObject
  36. @property (nonatomic) NSString *parentClassName;
  37. @property (nonatomic) NSString *key;
  38. @end
  39. @implementation RLMManagedArrayHandoverMetadata
  40. @end
  41. @interface RLMManagedArray () <RLMThreadConfined_Private>
  42. @end
  43. //
  44. // RLMArray implementation
  45. //
  46. @implementation RLMManagedArray {
  47. @public
  48. realm::List _backingList;
  49. RLMRealm *_realm;
  50. RLMClassInfo *_objectInfo;
  51. RLMClassInfo *_ownerInfo;
  52. std::unique_ptr<RLMObservationInfo> _observationInfo;
  53. }
  54. - (RLMManagedArray *)initWithList:(realm::List)list
  55. realm:(__unsafe_unretained RLMRealm *const)realm
  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 optional:property.optional];
  62. if (self) {
  63. _realm = realm;
  64. REALM_ASSERT(list.get_realm() == realm->_realm);
  65. _backingList = std::move(list);
  66. _ownerInfo = parentInfo;
  67. if (property.type == RLMPropertyTypeObject)
  68. _objectInfo = &parentInfo->linkTargetType(property.index);
  69. else
  70. _objectInfo = _ownerInfo;
  71. _key = property.name;
  72. }
  73. return self;
  74. }
  75. - (RLMManagedArray *)initWithParent:(__unsafe_unretained RLMObjectBase *const)parentObject
  76. property:(__unsafe_unretained RLMProperty *const)property {
  77. __unsafe_unretained RLMRealm *const realm = parentObject->_realm;
  78. auto col = parentObject->_info->tableColumn(property);
  79. auto& row = parentObject->_row;
  80. return [self initWithList:realm::List(realm->_realm, *row.get_table(), col, row.get_index())
  81. realm:realm
  82. parentInfo:parentObject->_info
  83. property:property];
  84. }
  85. void RLMValidateArrayObservationKey(__unsafe_unretained NSString *const keyPath,
  86. __unsafe_unretained RLMArray *const array) {
  87. if (![keyPath isEqualToString:RLMInvalidatedKey]) {
  88. @throw RLMException(@"[<%@ %p> addObserver:forKeyPath:options:context:] is not supported. Key path: %@",
  89. [array class], array, keyPath);
  90. }
  91. }
  92. void RLMEnsureArrayObservationInfo(std::unique_ptr<RLMObservationInfo>& info,
  93. __unsafe_unretained NSString *const keyPath,
  94. __unsafe_unretained RLMArray *const array,
  95. __unsafe_unretained id const observed) {
  96. RLMValidateArrayObservationKey(keyPath, array);
  97. if (!info && array.class == [RLMManagedArray class]) {
  98. auto lv = static_cast<RLMManagedArray *>(array);
  99. info = std::make_unique<RLMObservationInfo>(*lv->_ownerInfo,
  100. lv->_backingList.get_origin_row_index(),
  101. observed);
  102. }
  103. }
  104. //
  105. // validation helpers
  106. //
  107. [[gnu::noinline]]
  108. [[noreturn]]
  109. static void throwError(__unsafe_unretained RLMManagedArray *const ar, NSString *aggregateMethod) {
  110. try {
  111. throw;
  112. }
  113. catch (realm::InvalidTransactionException const&) {
  114. @throw RLMException(@"Cannot modify managed RLMArray outside of a write transaction.");
  115. }
  116. catch (realm::IncorrectThreadException const&) {
  117. @throw RLMException(@"Realm accessed from incorrect thread.");
  118. }
  119. catch (realm::List::InvalidatedException const&) {
  120. @throw RLMException(@"RLMArray has been invalidated or the containing object has been deleted.");
  121. }
  122. catch (realm::List::OutOfBoundsIndexException const& e) {
  123. @throw RLMException(@"Index %zu is out of bounds (must be less than %zu).",
  124. e.requested, e.valid_count);
  125. }
  126. catch (realm::Results::UnsupportedColumnTypeException const& e) {
  127. if (ar->_backingList.get_type() == realm::PropertyType::Object) {
  128. @throw RLMException(@"%@: is not supported for %s%s property '%s'.",
  129. aggregateMethod,
  130. string_for_property_type(e.property_type),
  131. is_nullable(e.property_type) ? "?" : "",
  132. e.column_name.data());
  133. }
  134. @throw RLMException(@"%@: is not supported for %s%s array '%@.%@'.",
  135. aggregateMethod,
  136. string_for_property_type(e.property_type),
  137. is_nullable(e.property_type) ? "?" : "",
  138. ar->_ownerInfo->rlmObjectSchema.className, ar->_key);
  139. }
  140. catch (std::logic_error const& e) {
  141. @throw RLMException(e);
  142. }
  143. }
  144. template<typename Function>
  145. static auto translateErrors(__unsafe_unretained RLMManagedArray *const ar,
  146. Function&& f, NSString *aggregateMethod=nil) {
  147. try {
  148. return f();
  149. }
  150. catch (...) {
  151. throwError(ar, aggregateMethod);
  152. }
  153. }
  154. template<typename Function>
  155. static auto translateErrors(Function&& f) {
  156. try {
  157. return f();
  158. }
  159. catch (...) {
  160. throwError(nil, nil);
  161. }
  162. }
  163. template<typename IndexSetFactory>
  164. static void changeArray(__unsafe_unretained RLMManagedArray *const ar,
  165. NSKeyValueChange kind, dispatch_block_t f, IndexSetFactory&& is) {
  166. translateErrors([&] { ar->_backingList.verify_in_transaction(); });
  167. RLMObservationInfo *info = RLMGetObservationInfo(ar->_observationInfo.get(),
  168. ar->_backingList.get_origin_row_index(),
  169. *ar->_ownerInfo);
  170. if (info) {
  171. NSIndexSet *indexes = is();
  172. info->willChange(ar->_key, kind, indexes);
  173. try {
  174. f();
  175. }
  176. catch (...) {
  177. info->didChange(ar->_key, kind, indexes);
  178. throwError(ar, nil);
  179. }
  180. info->didChange(ar->_key, kind, indexes);
  181. }
  182. else {
  183. translateErrors([&] { f(); });
  184. }
  185. }
  186. static void changeArray(__unsafe_unretained RLMManagedArray *const ar, NSKeyValueChange kind, NSUInteger index, dispatch_block_t f) {
  187. changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndex:index]; });
  188. }
  189. static void changeArray(__unsafe_unretained RLMManagedArray *const ar, NSKeyValueChange kind, NSRange range, dispatch_block_t f) {
  190. changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndexesInRange:range]; });
  191. }
  192. static void changeArray(__unsafe_unretained RLMManagedArray *const ar, NSKeyValueChange kind, NSIndexSet *is, dispatch_block_t f) {
  193. changeArray(ar, kind, f, [=] { return is; });
  194. }
  195. //
  196. // public method implementations
  197. //
  198. - (RLMRealm *)realm {
  199. return _realm;
  200. }
  201. - (NSUInteger)count {
  202. return translateErrors([&] { return _backingList.size(); });
  203. }
  204. - (BOOL)isInvalidated {
  205. return translateErrors([&] { return !_backingList.is_valid(); });
  206. }
  207. - (RLMClassInfo *)objectInfo {
  208. return _objectInfo;
  209. }
  210. - (bool)isBackedByList:(realm::List const&)list {
  211. return _backingList == list;
  212. }
  213. - (BOOL)isEqual:(id)object {
  214. return [object respondsToSelector:@selector(isBackedByList:)] && [object isBackedByList:_backingList];
  215. }
  216. - (NSUInteger)hash {
  217. return std::hash<realm::List>()(_backingList);
  218. }
  219. - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
  220. objects:(__unused __unsafe_unretained id [])buffer
  221. count:(NSUInteger)len {
  222. return RLMFastEnumerate(state, len, self);
  223. }
  224. - (id)objectAtIndex:(NSUInteger)index {
  225. return translateErrors([&] {
  226. RLMAccessorContext context(_realm, *_objectInfo);
  227. return _backingList.get(context, index);
  228. });
  229. }
  230. static void RLMInsertObject(RLMManagedArray *ar, id object, NSUInteger index) {
  231. RLMArrayValidateMatchingObjectType(ar, object);
  232. if (index == NSUIntegerMax) {
  233. index = translateErrors([&] { return ar->_backingList.size(); });
  234. }
  235. changeArray(ar, NSKeyValueChangeInsertion, index, ^{
  236. RLMAccessorContext context(ar->_realm, *ar->_objectInfo);
  237. ar->_backingList.insert(context, index, object);
  238. });
  239. }
  240. - (void)addObject:(id)object {
  241. RLMInsertObject(self, object, NSUIntegerMax);
  242. }
  243. - (void)insertObject:(id)object atIndex:(NSUInteger)index {
  244. RLMInsertObject(self, object, index);
  245. }
  246. - (void)insertObjects:(id<NSFastEnumeration>)objects atIndexes:(NSIndexSet *)indexes {
  247. changeArray(self, NSKeyValueChangeInsertion, indexes, ^{
  248. NSUInteger index = [indexes firstIndex];
  249. RLMAccessorContext context(_realm, *_objectInfo);
  250. for (id obj in objects) {
  251. RLMArrayValidateMatchingObjectType(self, obj);
  252. _backingList.insert(context, index, obj);
  253. index = [indexes indexGreaterThanIndex:index];
  254. }
  255. });
  256. }
  257. - (void)removeObjectAtIndex:(NSUInteger)index {
  258. changeArray(self, NSKeyValueChangeRemoval, index, ^{
  259. _backingList.remove(index);
  260. });
  261. }
  262. - (void)removeObjectsAtIndexes:(NSIndexSet *)indexes {
  263. changeArray(self, NSKeyValueChangeRemoval, indexes, ^{
  264. [indexes enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger idx, BOOL *) {
  265. _backingList.remove(idx);
  266. }];
  267. });
  268. }
  269. - (void)addObjectsFromArray:(NSArray *)array {
  270. changeArray(self, NSKeyValueChangeInsertion, NSMakeRange(self.count, array.count), ^{
  271. RLMAccessorContext context(_realm, *_objectInfo);
  272. for (id obj in array) {
  273. RLMArrayValidateMatchingObjectType(self, obj);
  274. _backingList.add(context, obj);
  275. }
  276. });
  277. }
  278. - (void)removeAllObjects {
  279. changeArray(self, NSKeyValueChangeRemoval, NSMakeRange(0, self.count), ^{
  280. _backingList.remove_all();
  281. });
  282. }
  283. - (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)object {
  284. RLMArrayValidateMatchingObjectType(self, object);
  285. changeArray(self, NSKeyValueChangeReplacement, index, ^{
  286. RLMAccessorContext context(_realm, *_objectInfo);
  287. _backingList.set(context, index, object);
  288. });
  289. }
  290. - (void)moveObjectAtIndex:(NSUInteger)sourceIndex toIndex:(NSUInteger)destinationIndex {
  291. auto start = std::min(sourceIndex, destinationIndex);
  292. auto len = std::max(sourceIndex, destinationIndex) - start + 1;
  293. changeArray(self, NSKeyValueChangeReplacement, {start, len}, ^{
  294. _backingList.move(sourceIndex, destinationIndex);
  295. });
  296. }
  297. - (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2 {
  298. changeArray(self, NSKeyValueChangeReplacement, ^{
  299. _backingList.swap(index1, index2);
  300. }, [=] {
  301. NSMutableIndexSet *set = [[NSMutableIndexSet alloc] initWithIndex:index1];
  302. [set addIndex:index2];
  303. return set;
  304. });
  305. }
  306. - (NSUInteger)indexOfObject:(id)object {
  307. RLMArrayValidateMatchingObjectType(self, object);
  308. return translateErrors([&] {
  309. RLMAccessorContext context(_realm, *_objectInfo);
  310. return RLMConvertNotFound(_backingList.find(context, object));
  311. });
  312. }
  313. - (id)valueForKeyPath:(NSString *)keyPath {
  314. if ([keyPath hasPrefix:@"@"]) {
  315. // Delegate KVC collection operators to RLMResults
  316. return translateErrors([&] {
  317. auto results = [RLMResults resultsWithObjectInfo:*_objectInfo results:_backingList.as_results()];
  318. return [results valueForKeyPath:keyPath];
  319. });
  320. }
  321. return [super valueForKeyPath:keyPath];
  322. }
  323. - (id)valueForKey:(NSString *)key {
  324. // Ideally we'd use "@invalidated" for this so that "invalidated" would use
  325. // normal array KVC semantics, but observing @things works very oddly (when
  326. // it's part of a key path, it's triggered automatically when array index
  327. // changes occur, and can't be sent explicitly, but works normally when it's
  328. // the entire key path), and an RLMManagedArray *can't* have objects where
  329. // invalidated is true, so we're not losing much.
  330. return translateErrors([&]() -> id {
  331. if ([key isEqualToString:RLMInvalidatedKey]) {
  332. return @(!_backingList.is_valid());
  333. }
  334. _backingList.verify_attached();
  335. return RLMCollectionValueForKey(_backingList, key, _realm, *_objectInfo);
  336. });
  337. }
  338. - (void)setValue:(id)value forKey:(NSString *)key {
  339. if ([key isEqualToString:@"self"]) {
  340. RLMArrayValidateMatchingObjectType(self, value);
  341. RLMAccessorContext context(_realm, *_objectInfo);
  342. translateErrors([&] {
  343. for (size_t i = 0, count = _backingList.size(); i < count; ++i) {
  344. _backingList.set(context, i, value);
  345. }
  346. });
  347. return;
  348. }
  349. else if (_type == RLMPropertyTypeObject) {
  350. RLMArrayValidateMatchingObjectType(self, value);
  351. translateErrors([&] { _backingList.verify_in_transaction(); });
  352. RLMCollectionSetValueForKey(self, key, value);
  353. }
  354. else {
  355. [self setValue:value forUndefinedKey:key];
  356. }
  357. }
  358. - (size_t)columnForProperty:(NSString *)propertyName {
  359. if (_backingList.get_type() == realm::PropertyType::Object) {
  360. return _objectInfo->tableColumn(propertyName);
  361. }
  362. if (![propertyName isEqualToString:@"self"]) {
  363. @throw RLMException(@"Arrays of '%@' can only be aggregated on \"self\"", RLMTypeToString(_type));
  364. }
  365. return 0;
  366. }
  367. - (id)minOfProperty:(NSString *)property {
  368. size_t column = [self columnForProperty:property];
  369. auto value = translateErrors(self, [&] { return _backingList.min(column); }, @"minOfProperty");
  370. return value ? RLMMixedToObjc(*value) : nil;
  371. }
  372. - (id)maxOfProperty:(NSString *)property {
  373. size_t column = [self columnForProperty:property];
  374. auto value = translateErrors(self, [&] { return _backingList.max(column); }, @"maxOfProperty");
  375. return value ? RLMMixedToObjc(*value) : nil;
  376. }
  377. - (id)sumOfProperty:(NSString *)property {
  378. size_t column = [self columnForProperty:property];
  379. return RLMMixedToObjc(translateErrors(self, [&] { return _backingList.sum(column); }, @"sumOfProperty"));
  380. }
  381. - (id)averageOfProperty:(NSString *)property {
  382. size_t column = [self columnForProperty:property];
  383. auto value = translateErrors(self, [&] { return _backingList.average(column); }, @"averageOfProperty");
  384. return value ? @(*value) : nil;
  385. }
  386. - (void)deleteObjectsFromRealm {
  387. if (_type != RLMPropertyTypeObject) {
  388. @throw RLMException(@"Cannot delete objects from RLMArray<%@>: only RLMObjects can be deleted.", RLMTypeToString(_type));
  389. }
  390. // delete all target rows from the realm
  391. RLMTrackDeletions(_realm, ^{
  392. translateErrors([&] { _backingList.delete_all(); });
  393. });
  394. }
  395. - (RLMResults *)sortedResultsUsingDescriptors:(NSArray<RLMSortDescriptor *> *)properties {
  396. return translateErrors([&] {
  397. return [RLMResults resultsWithObjectInfo:*_objectInfo
  398. results:_backingList.sort(RLMSortDescriptorsToKeypathArray(properties))];
  399. });
  400. }
  401. - (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
  402. if (_type != RLMPropertyTypeObject) {
  403. @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects");
  404. }
  405. auto query = RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema, _realm.schema, _realm.group);
  406. auto results = translateErrors([&] { return _backingList.filter(std::move(query)); });
  407. return [RLMResults resultsWithObjectInfo:*_objectInfo results:std::move(results)];
  408. }
  409. - (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate {
  410. if (_type != RLMPropertyTypeObject) {
  411. @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects");
  412. }
  413. realm::Query query = RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema,
  414. _realm.schema, _realm.group);
  415. return translateErrors([&] {
  416. return RLMConvertNotFound(_backingList.find(std::move(query)));
  417. });
  418. }
  419. - (NSArray *)objectsAtIndexes:(__unused NSIndexSet *)indexes {
  420. // FIXME: this is called by KVO when array changes are made. It's not clear
  421. // why, and returning nil seems to work fine.
  422. return nil;
  423. }
  424. - (void)addObserver:(id)observer
  425. forKeyPath:(NSString *)keyPath
  426. options:(NSKeyValueObservingOptions)options
  427. context:(void *)context {
  428. RLMEnsureArrayObservationInfo(_observationInfo, keyPath, self, self);
  429. [super addObserver:observer forKeyPath:keyPath options:options context:context];
  430. }
  431. - (realm::TableView)tableView {
  432. return translateErrors([&] { return _backingList.get_query(); }).find_all();
  433. }
  434. - (RLMFastEnumerator *)fastEnumerator {
  435. return translateErrors([&] {
  436. return [[RLMFastEnumerator alloc] initWithList:_backingList collection:self
  437. realm:_realm classInfo:*_objectInfo];
  438. });
  439. }
  440. // The compiler complains about the method's argument type not matching due to
  441. // it not having the generic type attached, but it doesn't seem to be possible
  442. // to actually include the generic type
  443. // http://www.openradar.me/radar?id=6135653276319744
  444. #pragma clang diagnostic push
  445. #pragma clang diagnostic ignored "-Wmismatched-parameter-types"
  446. - (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, RLMCollectionChange *, NSError *))block {
  447. [_realm verifyNotificationsAreSupported:true];
  448. return RLMAddNotificationBlock(self, _backingList, block);
  449. }
  450. #pragma clang diagnostic pop
  451. #pragma mark - Thread Confined Protocol Conformance
  452. - (std::unique_ptr<realm::ThreadSafeReferenceBase>)makeThreadSafeReference {
  453. auto list_reference = _realm->_realm->obtain_thread_safe_reference(_backingList);
  454. return std::make_unique<realm::ThreadSafeReference<realm::List>>(std::move(list_reference));
  455. }
  456. - (RLMManagedArrayHandoverMetadata *)objectiveCMetadata {
  457. RLMManagedArrayHandoverMetadata *metadata = [[RLMManagedArrayHandoverMetadata alloc] init];
  458. metadata.parentClassName = _ownerInfo->rlmObjectSchema.className;
  459. metadata.key = _key;
  460. return metadata;
  461. }
  462. + (instancetype)objectWithThreadSafeReference:(std::unique_ptr<realm::ThreadSafeReferenceBase>)reference
  463. metadata:(RLMManagedArrayHandoverMetadata *)metadata
  464. realm:(RLMRealm *)realm {
  465. REALM_ASSERT_DEBUG(dynamic_cast<realm::ThreadSafeReference<realm::List> *>(reference.get()));
  466. auto list_reference = static_cast<realm::ThreadSafeReference<realm::List> *>(reference.get());
  467. auto list = realm->_realm->resolve_thread_safe_reference(std::move(*list_reference));
  468. if (!list.is_valid()) {
  469. return nil;
  470. }
  471. RLMClassInfo *parentInfo = &realm->_info[metadata.parentClassName];
  472. return [[RLMManagedArray alloc] initWithList:std::move(list)
  473. realm:realm
  474. parentInfo:parentInfo
  475. property:parentInfo->rlmObjectSchema[metadata.key]];
  476. }
  477. @end