RLMManagedArray.mm 20 KB

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