RLMResults.mm 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  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 "RLMResults_Private.hpp"
  19. #import "RLMAccessor.hpp"
  20. #import "RLMArray_Private.hpp"
  21. #import "RLMCollection_Private.hpp"
  22. #import "RLMObjectSchema_Private.hpp"
  23. #import "RLMObjectStore.h"
  24. #import "RLMObject_Private.hpp"
  25. #import "RLMObservation.hpp"
  26. #import "RLMProperty_Private.h"
  27. #import "RLMQueryUtil.hpp"
  28. #import "RLMRealm_Private.hpp"
  29. #import "RLMSchema_Private.h"
  30. #import "RLMThreadSafeReference_Private.hpp"
  31. #import "RLMUtil.hpp"
  32. #import "results.hpp"
  33. #import "shared_realm.hpp"
  34. #import <objc/message.h>
  35. #import <realm/table_view.hpp>
  36. using namespace realm;
  37. #pragma clang diagnostic push
  38. #pragma clang diagnostic ignored "-Wincomplete-implementation"
  39. @implementation RLMNotificationToken
  40. @end
  41. #pragma clang diagnostic pop
  42. @interface RLMResults () <RLMThreadConfined_Private>
  43. @end
  44. //
  45. // RLMResults implementation
  46. //
  47. @implementation RLMResults {
  48. RLMRealm *_realm;
  49. RLMClassInfo *_info;
  50. }
  51. - (instancetype)initPrivate {
  52. self = [super init];
  53. return self;
  54. }
  55. - (instancetype)initWithResults:(Results)results {
  56. if (self = [super init]) {
  57. _results = std::move(results);
  58. }
  59. return self;
  60. }
  61. static void assertKeyPathIsNotNested(NSString *keyPath) {
  62. if ([keyPath rangeOfString:@"."].location != NSNotFound) {
  63. @throw RLMException(@"Nested key paths are not supported yet for KVC collection operators.");
  64. }
  65. }
  66. void RLMThrowResultsError(NSString *aggregateMethod) {
  67. try {
  68. throw;
  69. }
  70. catch (realm::InvalidTransactionException const&) {
  71. @throw RLMException(@"Cannot modify Results outside of a write transaction.");
  72. }
  73. catch (realm::IncorrectThreadException const&) {
  74. @throw RLMException(@"Realm accessed from incorrect thread.");
  75. }
  76. catch (realm::Results::InvalidatedException const&) {
  77. @throw RLMException(@"RLMResults has been invalidated.");
  78. }
  79. catch (realm::Results::DetatchedAccessorException const&) {
  80. @throw RLMException(@"Object has been invalidated.");
  81. }
  82. catch (realm::Results::IncorrectTableException const& e) {
  83. @throw RLMException(@"Object of type '%s' does not match RLMResults type '%s'.",
  84. e.actual.data(), e.expected.data());
  85. }
  86. catch (realm::Results::OutOfBoundsIndexException const& e) {
  87. @throw RLMException(@"Index %zu is out of bounds (must be less than %zu).",
  88. e.requested, e.valid_count);
  89. }
  90. catch (realm::Results::UnsupportedColumnTypeException const& e) {
  91. @throw RLMException(@"%@ is not supported for %s%s property '%s'.",
  92. aggregateMethod,
  93. string_for_property_type(e.property_type),
  94. is_nullable(e.property_type) ? "?" : "",
  95. e.column_name.data());
  96. }
  97. catch (std::exception const& e) {
  98. @throw RLMException(e);
  99. }
  100. }
  101. - (instancetype)initWithObjectInfo:(RLMClassInfo&)info
  102. results:(realm::Results&&)results {
  103. if (self = [super init]) {
  104. _results = std::move(results);
  105. _realm = info.realm;
  106. _info = &info;
  107. }
  108. return self;
  109. }
  110. + (instancetype)resultsWithObjectInfo:(RLMClassInfo&)info
  111. results:(realm::Results&&)results {
  112. return [[self alloc] initWithObjectInfo:info results:std::move(results)];
  113. }
  114. + (instancetype)emptyDetachedResults {
  115. return [[self alloc] initPrivate];
  116. }
  117. - (instancetype)subresultsWithResults:(realm::Results)results {
  118. return [self.class resultsWithObjectInfo:*_info results:std::move(results)];
  119. }
  120. static inline void RLMResultsValidateInWriteTransaction(__unsafe_unretained RLMResults *const ar) {
  121. ar->_realm->_realm->verify_thread();
  122. ar->_realm->_realm->verify_in_write();
  123. }
  124. - (BOOL)isInvalidated {
  125. return translateRLMResultsErrors([&] { return !_results.is_valid(); });
  126. }
  127. - (NSUInteger)count {
  128. return translateRLMResultsErrors([&] { return _results.size(); });
  129. }
  130. - (RLMPropertyType)type {
  131. return translateRLMResultsErrors([&] {
  132. return static_cast<RLMPropertyType>(_results.get_type() & ~realm::PropertyType::Nullable);
  133. });
  134. }
  135. - (BOOL)isOptional {
  136. return translateRLMResultsErrors([&] {
  137. return is_nullable(_results.get_type());
  138. });
  139. }
  140. - (NSString *)objectClassName {
  141. return translateRLMResultsErrors([&] {
  142. if (_info && _results.get_type() == realm::PropertyType::Object) {
  143. return _info->rlmObjectSchema.className;
  144. }
  145. return (NSString *)nil;
  146. });
  147. }
  148. - (RLMClassInfo *)objectInfo {
  149. return _info;
  150. }
  151. - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
  152. objects:(__unused __unsafe_unretained id [])buffer
  153. count:(NSUInteger)len {
  154. if (!_info) {
  155. return 0;
  156. }
  157. if (state->state == 0) {
  158. translateRLMResultsErrors([&] {
  159. _results.evaluate_query_if_needed();
  160. });
  161. }
  162. return RLMFastEnumerate(state, len, self);
  163. }
  164. - (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ... {
  165. va_list args;
  166. va_start(args, predicateFormat);
  167. NSUInteger index = [self indexOfObjectWhere:predicateFormat args:args];
  168. va_end(args);
  169. return index;
  170. }
  171. - (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args {
  172. return [self indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:predicateFormat
  173. arguments:args]];
  174. }
  175. - (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate {
  176. if (_results.get_mode() == Results::Mode::Empty) {
  177. return NSNotFound;
  178. }
  179. return translateRLMResultsErrors([&] {
  180. if (_results.get_type() != realm::PropertyType::Object) {
  181. @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects");
  182. }
  183. return RLMConvertNotFound(_results.index_of(RLMPredicateToQuery(predicate, _info->rlmObjectSchema, _realm.schema, _realm.group)));
  184. });
  185. }
  186. - (id)objectAtIndex:(NSUInteger)index {
  187. RLMAccessorContext ctx(*_info);
  188. return translateRLMResultsErrors([&] {
  189. return _results.get(ctx, index);
  190. });
  191. }
  192. - (id)firstObject {
  193. if (!_info) {
  194. return nil;
  195. }
  196. RLMAccessorContext ctx(*_info);
  197. return translateRLMResultsErrors([&] {
  198. return _results.first(ctx);
  199. });
  200. }
  201. - (id)lastObject {
  202. if (!_info) {
  203. return nil;
  204. }
  205. RLMAccessorContext ctx(*_info);
  206. return translateRLMResultsErrors([&] {
  207. return _results.last(ctx);
  208. });
  209. }
  210. - (NSUInteger)indexOfObject:(id)object {
  211. if (!_info || !object) {
  212. return NSNotFound;
  213. }
  214. if (RLMObjectBase *obj = RLMDynamicCast<RLMObjectBase>(object)) {
  215. // Unmanaged objects are considered not equal to all managed objects
  216. if (!obj->_realm && !obj.invalidated) {
  217. return NSNotFound;
  218. }
  219. }
  220. RLMAccessorContext ctx(*_info);
  221. return translateRLMResultsErrors([&] {
  222. return RLMConvertNotFound(_results.index_of(ctx, object));
  223. });
  224. }
  225. - (id)valueForKeyPath:(NSString *)keyPath {
  226. if ([keyPath characterAtIndex:0] != '@') {
  227. return [super valueForKeyPath:keyPath];
  228. }
  229. if ([keyPath isEqualToString:@"@count"]) {
  230. return @(self.count);
  231. }
  232. NSRange operatorRange = [keyPath rangeOfString:@"." options:NSLiteralSearch];
  233. NSUInteger keyPathLength = keyPath.length;
  234. NSUInteger separatorIndex = operatorRange.location != NSNotFound ? operatorRange.location : keyPathLength;
  235. NSString *operatorName = [keyPath substringWithRange:NSMakeRange(1, separatorIndex - 1)];
  236. SEL opSelector = NSSelectorFromString([NSString stringWithFormat:@"_%@ForKeyPath:", operatorName]);
  237. if (![self respondsToSelector:opSelector]) {
  238. @throw RLMException(@"Unsupported KVC collection operator found in key path '%@'", keyPath);
  239. }
  240. if (separatorIndex >= keyPathLength - 1) {
  241. @throw RLMException(@"Missing key path for KVC collection operator %@ in key path '%@'",
  242. operatorName, keyPath);
  243. }
  244. NSString *operatorKeyPath = [keyPath substringFromIndex:separatorIndex + 1];
  245. return ((id(*)(id, SEL, id))objc_msgSend)(self, opSelector, operatorKeyPath);
  246. }
  247. - (id)valueForKey:(NSString *)key {
  248. if (!_info) {
  249. return @[];
  250. }
  251. return translateRLMResultsErrors([&] {
  252. return RLMCollectionValueForKey(_results, key, *_info);
  253. });
  254. }
  255. - (void)setValue:(id)value forKey:(NSString *)key {
  256. translateRLMResultsErrors([&] { RLMResultsValidateInWriteTransaction(self); });
  257. RLMCollectionSetValueForKey(self, key, value);
  258. }
  259. - (NSNumber *)_aggregateForKeyPath:(NSString *)keyPath
  260. method:(util::Optional<Mixed> (Results::*)(ColKey))method
  261. methodName:(NSString *)methodName returnNilForEmpty:(BOOL)returnNilForEmpty {
  262. assertKeyPathIsNotNested(keyPath);
  263. return [self aggregate:keyPath method:method methodName:methodName returnNilForEmpty:returnNilForEmpty];
  264. }
  265. - (NSNumber *)_minForKeyPath:(NSString *)keyPath {
  266. return [self _aggregateForKeyPath:keyPath method:&Results::min methodName:@"@min" returnNilForEmpty:YES];
  267. }
  268. - (NSNumber *)_maxForKeyPath:(NSString *)keyPath {
  269. return [self _aggregateForKeyPath:keyPath method:&Results::max methodName:@"@max" returnNilForEmpty:YES];
  270. }
  271. - (NSNumber *)_sumForKeyPath:(NSString *)keyPath {
  272. return [self _aggregateForKeyPath:keyPath method:&Results::sum methodName:@"@sum" returnNilForEmpty:NO];
  273. }
  274. - (NSNumber *)_avgForKeyPath:(NSString *)keyPath {
  275. assertKeyPathIsNotNested(keyPath);
  276. return [self averageOfProperty:keyPath];
  277. }
  278. - (NSArray *)_unionOfObjectsForKeyPath:(NSString *)keyPath {
  279. assertKeyPathIsNotNested(keyPath);
  280. return translateRLMResultsErrors([&] {
  281. return RLMCollectionValueForKey(_results, keyPath, *_info);
  282. });
  283. }
  284. - (NSArray *)_distinctUnionOfObjectsForKeyPath:(NSString *)keyPath {
  285. return [NSSet setWithArray:[self _unionOfObjectsForKeyPath:keyPath]].allObjects;
  286. }
  287. - (NSArray *)_unionOfArraysForKeyPath:(NSString *)keyPath {
  288. assertKeyPathIsNotNested(keyPath);
  289. if ([keyPath isEqualToString:@"self"]) {
  290. @throw RLMException(@"self is not a valid key-path for a KVC array collection operator as 'unionOfArrays'.");
  291. }
  292. return translateRLMResultsErrors([&] {
  293. NSMutableArray *flatArray = [NSMutableArray new];
  294. for (id<NSFastEnumeration> array in RLMCollectionValueForKey(_results, keyPath, *_info)) {
  295. for (id value in array) {
  296. [flatArray addObject:value];
  297. }
  298. }
  299. return flatArray;
  300. });
  301. }
  302. - (NSArray *)_distinctUnionOfArraysForKeyPath:(__unused NSString *)keyPath {
  303. return [NSSet setWithArray:[self _unionOfArraysForKeyPath:keyPath]].allObjects;
  304. }
  305. - (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... {
  306. va_list args;
  307. va_start(args, predicateFormat);
  308. RLMResults *results = [self objectsWhere:predicateFormat args:args];
  309. va_end(args);
  310. return results;
  311. }
  312. - (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args {
  313. return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
  314. }
  315. - (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
  316. return translateRLMResultsErrors([&] {
  317. if (_results.get_mode() == Results::Mode::Empty) {
  318. return self;
  319. }
  320. if (_results.get_type() != realm::PropertyType::Object) {
  321. @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects");
  322. }
  323. auto query = RLMPredicateToQuery(predicate, _info->rlmObjectSchema, _realm.schema, _realm.group);
  324. return [self subresultsWithResults:_results.filter(std::move(query))];
  325. });
  326. }
  327. - (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending {
  328. return [self sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:keyPath ascending:ascending]]];
  329. }
  330. - (RLMResults *)sortedResultsUsingDescriptors:(NSArray<RLMSortDescriptor *> *)properties {
  331. if (properties.count == 0) {
  332. return self;
  333. }
  334. return translateRLMResultsErrors([&] {
  335. if (_results.get_mode() == Results::Mode::Empty) {
  336. return self;
  337. }
  338. return [self subresultsWithResults:_results.sort(RLMSortDescriptorsToKeypathArray(properties))];
  339. });
  340. }
  341. - (RLMResults *)distinctResultsUsingKeyPaths:(NSArray<NSString *> *)keyPaths {
  342. for (NSString *keyPath in keyPaths) {
  343. if ([keyPath rangeOfString:@"@"].location != NSNotFound) {
  344. @throw RLMException(@"Cannot distinct on keypath '%@': KVC collection operators are not supported.", keyPath);
  345. }
  346. }
  347. return translateRLMResultsErrors([&] {
  348. if (_results.get_mode() == Results::Mode::Empty) {
  349. return self;
  350. }
  351. std::vector<std::string> keyPathsVector;
  352. for (NSString *keyPath in keyPaths) {
  353. keyPathsVector.push_back(keyPath.UTF8String);
  354. }
  355. return [self subresultsWithResults:_results.distinct(keyPathsVector)];
  356. });
  357. }
  358. - (id)objectAtIndexedSubscript:(NSUInteger)index {
  359. return [self objectAtIndex:index];
  360. }
  361. - (id)aggregate:(NSString *)property method:(util::Optional<Mixed> (Results::*)(ColKey))method
  362. methodName:(NSString *)methodName returnNilForEmpty:(BOOL)returnNilForEmpty {
  363. if (_results.get_mode() == Results::Mode::Empty) {
  364. return returnNilForEmpty ? nil : @0;
  365. }
  366. ColKey column;
  367. if (self.type == RLMPropertyTypeObject || ![property isEqualToString:@"self"]) {
  368. column = _info->tableColumn(property);
  369. }
  370. auto value = translateRLMResultsErrors([&] { return (_results.*method)(column); }, methodName);
  371. return value ? RLMMixedToObjc(*value) : nil;
  372. }
  373. - (id)minOfProperty:(NSString *)property {
  374. return [self aggregate:property method:&Results::min
  375. methodName:@"minOfProperty" returnNilForEmpty:YES];
  376. }
  377. - (id)maxOfProperty:(NSString *)property {
  378. return [self aggregate:property method:&Results::max
  379. methodName:@"maxOfProperty" returnNilForEmpty:YES];
  380. }
  381. - (id)sumOfProperty:(NSString *)property {
  382. return [self aggregate:property method:&Results::sum
  383. methodName:@"sumOfProperty" returnNilForEmpty:NO];
  384. }
  385. - (id)averageOfProperty:(NSString *)property {
  386. if (_results.get_mode() == Results::Mode::Empty) {
  387. return nil;
  388. }
  389. ColKey column;
  390. if (self.type == RLMPropertyTypeObject || ![property isEqualToString:@"self"]) {
  391. column = _info->tableColumn(property);
  392. }
  393. auto value = translateRLMResultsErrors([&] { return _results.average(column); }, @"averageOfProperty");
  394. return value ? @(*value) : nil;
  395. }
  396. - (void)deleteObjectsFromRealm {
  397. if (self.type != RLMPropertyTypeObject) {
  398. @throw RLMException(@"Cannot delete objects from RLMResults<%@>: only RLMObjects can be deleted.",
  399. RLMTypeToString(self.type));
  400. }
  401. return translateRLMResultsErrors([&] {
  402. if (_results.get_mode() == Results::Mode::Table) {
  403. RLMResultsValidateInWriteTransaction(self);
  404. RLMClearTable(*_info);
  405. }
  406. else {
  407. RLMTrackDeletions(_realm, [&] { _results.clear(); });
  408. }
  409. });
  410. }
  411. - (NSString *)description {
  412. return RLMDescriptionWithMaxDepth(@"RLMResults", self, RLMDescriptionMaxDepth);
  413. }
  414. - (realm::TableView)tableView {
  415. return translateRLMResultsErrors([&] { return _results.get_tableview(); });
  416. }
  417. - (RLMFastEnumerator *)fastEnumerator {
  418. return translateRLMResultsErrors([&] {
  419. return [[RLMFastEnumerator alloc] initWithResults:_results collection:self
  420. classInfo:*_info];
  421. });
  422. }
  423. - (RLMResults *)snapshot {
  424. return translateRLMResultsErrors([&] {
  425. return [self subresultsWithResults:_results.snapshot()];
  426. });
  427. }
  428. - (instancetype)freeze {
  429. if (self.frozen) {
  430. return self;
  431. }
  432. RLMRealm *frozenRealm = [_realm freeze];
  433. return translateRLMResultsErrors([&] {
  434. return [self.class resultsWithObjectInfo:_info->freeze(frozenRealm)
  435. results:_results.freeze(frozenRealm->_realm)];
  436. });
  437. }
  438. - (BOOL)isFrozen {
  439. return _realm.frozen;
  440. }
  441. // The compiler complains about the method's argument type not matching due to
  442. // it not having the generic type attached, but it doesn't seem to be possible
  443. // to actually include the generic type
  444. // http://www.openradar.me/radar?id=6135653276319744
  445. #pragma clang diagnostic push
  446. #pragma clang diagnostic ignored "-Wmismatched-parameter-types"
  447. - (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMResults *, RLMCollectionChange *, NSError *))block {
  448. return RLMAddNotificationBlock(self, block, nil);
  449. }
  450. - (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMResults *, RLMCollectionChange *, NSError *))block queue:(dispatch_queue_t)queue {
  451. return RLMAddNotificationBlock(self, block, queue);
  452. }
  453. #pragma clang diagnostic pop
  454. realm::Results& RLMGetBackingCollection(RLMResults *self) {
  455. return self->_results;
  456. }
  457. - (BOOL)isAttached {
  458. return !!_realm;
  459. }
  460. #pragma mark - Thread Confined Protocol Conformance
  461. - (realm::ThreadSafeReference)makeThreadSafeReference {
  462. return _results;
  463. }
  464. - (id)objectiveCMetadata {
  465. return nil;
  466. }
  467. + (instancetype)objectWithThreadSafeReference:(realm::ThreadSafeReference)reference
  468. metadata:(__unused id)metadata
  469. realm:(RLMRealm *)realm {
  470. auto results = reference.resolve<Results>(realm->_realm);
  471. return [RLMResults resultsWithObjectInfo:realm->_info[RLMStringDataToNSString(results.get_object_type())]
  472. results:std::move(results)];
  473. }
  474. @end
  475. @implementation RLMLinkingObjects
  476. - (NSString *)description {
  477. return RLMDescriptionWithMaxDepth(@"RLMLinkingObjects", self, RLMDescriptionMaxDepth);
  478. }
  479. @end