PerformanceTests.m 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  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 "RLMTestCase.h"
  19. #import "RLMRealm_Dynamic.h"
  20. #import "RLMRealm_Private.h"
  21. #if !DEBUG && TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
  22. @interface PerformanceTests : RLMTestCase
  23. @property (nonatomic) dispatch_queue_t queue;
  24. @property (nonatomic) dispatch_semaphore_t sema;
  25. @end
  26. static RLMRealm *s_smallRealm, *s_mediumRealm, *s_largeRealm;
  27. @implementation PerformanceTests
  28. + (void)setUp {
  29. [super setUp];
  30. s_smallRealm = [self createStringObjects:1];
  31. s_mediumRealm = [self createStringObjects:5];
  32. s_largeRealm = [self createStringObjects:50];
  33. }
  34. + (void)tearDown {
  35. s_smallRealm = s_mediumRealm = s_largeRealm = nil;
  36. [RLMRealm resetRealmState];
  37. [super tearDown];
  38. }
  39. - (void)resetRealmState {
  40. // Do nothing, as we need to keep our in-memory realms around between tests
  41. }
  42. - (void)measureBlock:(void (^)(void))block {
  43. [super measureBlock:^{
  44. @autoreleasepool {
  45. block();
  46. }
  47. }];
  48. }
  49. - (void)measureMetrics:(NSArray *)metrics automaticallyStartMeasuring:(BOOL)automaticallyStartMeasuring forBlock:(void (^)(void))block {
  50. [super measureMetrics:metrics automaticallyStartMeasuring:automaticallyStartMeasuring forBlock:^{
  51. @autoreleasepool {
  52. block();
  53. }
  54. }];
  55. }
  56. + (RLMRealm *)createStringObjects:(int)factor {
  57. RLMRealmConfiguration *config = [RLMRealmConfiguration new];
  58. config.inMemoryIdentifier = @(factor).stringValue;
  59. RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
  60. [realm beginWriteTransaction];
  61. for (int i = 0; i < 1000 * factor; ++i) {
  62. [StringObject createInRealm:realm withValue:@[@"a"]];
  63. [StringObject createInRealm:realm withValue:@[@"b"]];
  64. }
  65. [realm commitWriteTransaction];
  66. return realm;
  67. }
  68. - (RLMRealm *)testRealm {
  69. RLMRealmConfiguration *config = [RLMRealmConfiguration new];
  70. config.inMemoryIdentifier = @"test";
  71. return [RLMRealm realmWithConfiguration:config error:nil];
  72. }
  73. - (void)testInsertMultiple {
  74. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  75. RLMRealm *realm = self.realmWithTestPath;
  76. [self startMeasuring];
  77. [realm beginWriteTransaction];
  78. for (int i = 0; i < 5000; ++i) {
  79. StringObject *obj = [[StringObject alloc] init];
  80. obj.stringCol = @"a";
  81. [realm addObject:obj];
  82. }
  83. [realm commitWriteTransaction];
  84. [self stopMeasuring];
  85. [self tearDown];
  86. }];
  87. }
  88. - (void)testInsertSingleLiteral {
  89. [self measureBlock:^{
  90. RLMRealm *realm = self.realmWithTestPath;
  91. for (int i = 0; i < 50; ++i) {
  92. [realm beginWriteTransaction];
  93. [StringObject createInRealm:realm withValue:@[@"a"]];
  94. [realm commitWriteTransaction];
  95. }
  96. [self tearDown];
  97. }];
  98. }
  99. - (void)testInsertMultipleLiteral {
  100. [self measureBlock:^{
  101. RLMRealm *realm = self.realmWithTestPath;
  102. [realm beginWriteTransaction];
  103. for (int i = 0; i < 5000; ++i) {
  104. [StringObject createInRealm:realm withValue:@[@"a"]];
  105. }
  106. [realm commitWriteTransaction];
  107. [self tearDown];
  108. }];
  109. }
  110. - (RLMRealm *)getStringObjects:(int)factor {
  111. RLMRealmConfiguration *config = [RLMRealmConfiguration new];
  112. config.inMemoryIdentifier = @(factor).stringValue;
  113. RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
  114. [NSFileManager.defaultManager removeItemAtURL:RLMTestRealmURL() error:nil];
  115. [realm writeCopyToURL:RLMTestRealmURL() encryptionKey:nil error:nil];
  116. return [self realmWithTestPath];
  117. }
  118. - (void)testCountWhereQuery {
  119. RLMRealm *realm = [self getStringObjects:50];
  120. [self measureBlock:^{
  121. for (int i = 0; i < 50; ++i) {
  122. RLMResults *array = [StringObject objectsInRealm:realm where:@"stringCol = 'a'"];
  123. [array count];
  124. }
  125. }];
  126. }
  127. - (void)testCountWhereTableView {
  128. RLMRealm *realm = [self getStringObjects:50];
  129. [self measureBlock:^{
  130. for (int i = 0; i < 10; ++i) {
  131. RLMResults *array = [StringObject objectsInRealm:realm where:@"stringCol = 'a'"];
  132. [array firstObject]; // Force materialization of backing table view
  133. [array count];
  134. }
  135. }];
  136. }
  137. - (void)testEnumerateAndAccessQuery {
  138. RLMRealm *realm = [self getStringObjects:5];
  139. [self measureBlock:^{
  140. for (StringObject *so in [StringObject objectsInRealm:realm where:@"stringCol = 'a'"]) {
  141. (void)[so stringCol];
  142. }
  143. }];
  144. }
  145. - (void)testEnumerateAndAccessAll {
  146. RLMRealm *realm = [self getStringObjects:5];
  147. [self measureBlock:^{
  148. for (StringObject *so in [StringObject allObjectsInRealm:realm]) {
  149. (void)[so stringCol];
  150. }
  151. }];
  152. }
  153. - (void)testEnumerateAndAccessAllSlow {
  154. RLMRealm *realm = [self getStringObjects:5];
  155. [self measureBlock:^{
  156. RLMResults *all = [StringObject allObjectsInRealm:realm];
  157. for (NSUInteger i = 0; i < all.count; ++i) {
  158. (void)[all[i] stringCol];
  159. }
  160. }];
  161. }
  162. - (void)testEnumerateAndAccessArrayProperty {
  163. RLMRealm *realm = [self getStringObjects:5];
  164. [realm beginWriteTransaction];
  165. ArrayPropertyObject *apo = [ArrayPropertyObject createInRealm:realm
  166. withValue:@[@"name", [StringObject allObjectsInRealm:realm], @[]]];
  167. [realm commitWriteTransaction];
  168. [self measureBlock:^{
  169. for (StringObject *so in apo.array) {
  170. (void)[so stringCol];
  171. }
  172. }];
  173. }
  174. - (void)testEnumerateAndAccessArrayPropertySlow {
  175. RLMRealm *realm = [self getStringObjects:5];
  176. [realm beginWriteTransaction];
  177. ArrayPropertyObject *apo = [ArrayPropertyObject createInRealm:realm
  178. withValue:@[@"name", [StringObject allObjectsInRealm:realm], @[]]];
  179. [realm commitWriteTransaction];
  180. [self measureBlock:^{
  181. RLMArray *array = apo.array;
  182. for (NSUInteger i = 0; i < array.count; ++i) {
  183. (void)[array[i] stringCol];
  184. }
  185. }];
  186. }
  187. - (void)testEnumerateAndMutateAll {
  188. RLMRealm *realm = [self getStringObjects:5];
  189. [self measureBlock:^{
  190. [realm beginWriteTransaction];
  191. for (StringObject *so in [StringObject allObjectsInRealm:realm]) {
  192. so.stringCol = @"c";
  193. }
  194. [realm commitWriteTransaction];
  195. }];
  196. }
  197. - (void)testEnumerateAndMutateQuery {
  198. RLMRealm *realm = [self getStringObjects:1];
  199. [self measureBlock:^{
  200. [realm beginWriteTransaction];
  201. for (StringObject *so in [StringObject objectsInRealm:realm where:@"stringCol != 'b'"]) {
  202. so.stringCol = @"c";
  203. }
  204. [realm commitWriteTransaction];
  205. }];
  206. }
  207. - (void)testQueryConstruction {
  208. RLMRealm *realm = self.realmWithTestPath;
  209. NSPredicate *predicate = [NSPredicate predicateWithFormat:@"boolCol = false and (intCol = 5 or floatCol = 1.0) and objectCol = nil and longCol != 7 and stringCol IN {'a', 'b', 'c'}"];
  210. [self measureBlock:^{
  211. for (int i = 0; i < 500; ++i) {
  212. [AllTypesObject objectsInRealm:realm withPredicate:predicate];
  213. }
  214. }];
  215. }
  216. - (void)testDeleteAll {
  217. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  218. RLMRealm *realm = [self getStringObjects:50];
  219. [self startMeasuring];
  220. [realm beginWriteTransaction];
  221. [realm deleteObjects:[StringObject allObjectsInRealm:realm]];
  222. [realm commitWriteTransaction];
  223. [self stopMeasuring];
  224. }];
  225. }
  226. - (void)testQueryDeletion {
  227. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  228. RLMRealm *realm = [self getStringObjects:5];
  229. [self startMeasuring];
  230. [realm beginWriteTransaction];
  231. [realm deleteObjects:[StringObject objectsInRealm:realm where:@"stringCol = 'a' OR stringCol = 'b'"]];
  232. [realm commitWriteTransaction];
  233. [self stopMeasuring];
  234. }];
  235. }
  236. - (void)testManualDeletion {
  237. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  238. RLMRealm *realm = [self getStringObjects:5];
  239. NSMutableArray *objects = [NSMutableArray arrayWithCapacity:10000];
  240. for (StringObject *obj in [StringObject allObjectsInRealm:realm]) {
  241. [objects addObject:obj];
  242. }
  243. [self startMeasuring];
  244. [realm beginWriteTransaction];
  245. [realm deleteObjects:objects];
  246. [realm commitWriteTransaction];
  247. [self stopMeasuring];
  248. }];
  249. }
  250. - (void)testUnIndexedStringLookup {
  251. RLMRealm *realm = self.realmWithTestPath;
  252. [realm beginWriteTransaction];
  253. for (int i = 0; i < 1000; ++i) {
  254. [StringObject createInRealm:realm withValue:@[@(i).stringValue]];
  255. }
  256. [realm commitWriteTransaction];
  257. [self measureBlock:^{
  258. for (int i = 0; i < 1000; ++i) {
  259. [[StringObject objectsInRealm:realm where:@"stringCol = %@", @(i).stringValue] firstObject];
  260. }
  261. }];
  262. }
  263. - (void)testIndexedStringLookup {
  264. RLMRealm *realm = self.realmWithTestPath;
  265. [realm beginWriteTransaction];
  266. for (int i = 0; i < 1000; ++i) {
  267. [IndexedStringObject createInRealm:realm withValue:@[@(i).stringValue]];
  268. }
  269. [realm commitWriteTransaction];
  270. [self measureBlock:^{
  271. for (int i = 0; i < 1000; ++i) {
  272. [[IndexedStringObject objectsInRealm:realm where:@"stringCol = %@", @(i).stringValue] firstObject];
  273. }
  274. }];
  275. }
  276. - (void)testLargeINQuery {
  277. RLMRealm *realm = self.realmWithTestPath;
  278. [realm beginWriteTransaction];
  279. NSMutableArray *ids = [NSMutableArray arrayWithCapacity:3000];
  280. for (int i = 0; i < 3000; ++i) {
  281. [IntObject createInRealm:realm withValue:@[@(i)]];
  282. if (i % 2) {
  283. [ids addObject:@(i)];
  284. }
  285. }
  286. [realm commitWriteTransaction];
  287. [self measureBlock:^{
  288. (void)[[IntObject objectsInRealm:realm where:@"intCol IN %@", ids] firstObject];
  289. }];
  290. }
  291. - (void)testSortingAllObjects {
  292. RLMRealm *realm = self.realmWithTestPath;
  293. [realm beginWriteTransaction];
  294. for (int i = 0; i < 3000; ++i) {
  295. [IntObject createInRealm:realm withValue:@[@(arc4random())]];
  296. }
  297. [realm commitWriteTransaction];
  298. [self measureBlock:^{
  299. (void)[[IntObject allObjectsInRealm:realm] sortedResultsUsingProperty:@"intCol" ascending:YES].lastObject;
  300. }];
  301. }
  302. - (void)testRealmCreationCached {
  303. __block RLMRealm *realm;
  304. [self dispatchAsyncAndWait:^{
  305. realm = [self realmWithTestPath]; // ensure a cached realm for the path
  306. }];
  307. [self measureBlock:^{
  308. for (int i = 0; i < 250; ++i) {
  309. @autoreleasepool {
  310. [self realmWithTestPath];
  311. }
  312. }
  313. }];
  314. [realm configuration];
  315. }
  316. - (void)testRealmCreationUncached {
  317. [self measureBlock:^{
  318. for (int i = 0; i < 50; ++i) {
  319. @autoreleasepool {
  320. [self realmWithTestPath];
  321. }
  322. }
  323. }];
  324. }
  325. - (void)testRealmFileCreation {
  326. RLMRealmConfiguration *config = [RLMRealmConfiguration new];
  327. __block int measurement = 0;
  328. const int iterations = 10;
  329. [self measureBlock:^{
  330. for (int i = 0; i < iterations; ++i) {
  331. @autoreleasepool {
  332. config.inMemoryIdentifier = @(measurement * iterations + i).stringValue;
  333. [RLMRealm realmWithConfiguration:config error:nil];
  334. }
  335. }
  336. ++measurement;
  337. }];
  338. }
  339. - (void)testInvalidateRefresh {
  340. RLMRealm *realm = [self testRealm];
  341. [self measureBlock:^{
  342. for (int i = 0; i < 50000; ++i) {
  343. @autoreleasepool {
  344. [realm invalidate];
  345. [realm refresh];
  346. }
  347. }
  348. }];
  349. }
  350. - (void)testCommitWriteTransaction {
  351. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  352. RLMRealm *realm = self.testRealm;
  353. [realm beginWriteTransaction];
  354. IntObject *obj = [IntObject createInRealm:realm withValue:@[@0]];
  355. [realm commitWriteTransaction];
  356. [self startMeasuring];
  357. while (obj.intCol < 100) {
  358. [realm transactionWithBlock:^{
  359. obj.intCol++;
  360. }];
  361. }
  362. [self stopMeasuring];
  363. }];
  364. }
  365. - (void)testCommitWriteTransactionWithLocalNotification {
  366. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  367. RLMRealm *realm = self.testRealm;
  368. [realm beginWriteTransaction];
  369. IntObject *obj = [IntObject createInRealm:realm withValue:@[@0]];
  370. [realm commitWriteTransaction];
  371. RLMNotificationToken *token = [realm addNotificationBlock:^(__unused NSString *note, __unused RLMRealm *realm) { }];
  372. [self startMeasuring];
  373. while (obj.intCol < 500) {
  374. [realm transactionWithBlock:^{
  375. obj.intCol++;
  376. }];
  377. }
  378. [self stopMeasuring];
  379. [token invalidate];
  380. }];
  381. }
  382. - (void)testCommitWriteTransactionWithCrossThreadNotification {
  383. const int stopValue = 500;
  384. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  385. RLMRealm *realm = self.testRealm;
  386. [realm beginWriteTransaction];
  387. IntObject *obj = [IntObject createInRealm:realm withValue:@[@0]];
  388. [realm commitWriteTransaction];
  389. dispatch_semaphore_t sema = dispatch_semaphore_create(0);
  390. [self dispatchAsync:^{
  391. RLMRealm *realm = self.testRealm;
  392. IntObject *obj = [[IntObject allObjectsInRealm:realm] firstObject];
  393. __block RLMNotificationToken *token;
  394. CFRunLoopPerformBlock(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, ^{
  395. token = [realm addNotificationBlock:^(__unused NSString *note, __unused RLMRealm *realm) {
  396. if (obj.intCol == stopValue) {
  397. CFRunLoopStop(CFRunLoopGetCurrent());
  398. }
  399. }];
  400. dispatch_semaphore_signal(sema);
  401. });
  402. CFRunLoopRun();
  403. [token invalidate];
  404. }];
  405. dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
  406. [self startMeasuring];
  407. while (obj.intCol < stopValue) {
  408. [realm transactionWithBlock:^{
  409. obj.intCol++;
  410. }];
  411. }
  412. [self dispatchAsyncAndWait:^{}];
  413. [self stopMeasuring];
  414. }];
  415. }
  416. - (void)testCommitWriteTransactionWithResultsNotification {
  417. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  418. RLMRealm *realm = [self getStringObjects:5];
  419. RLMResults *results = [StringObject allObjectsInRealm:realm];
  420. id token = [results addNotificationBlock:^(__unused RLMResults *results, __unused RLMCollectionChange *change, __unused NSError *error) {
  421. CFRunLoopStop(CFRunLoopGetCurrent());
  422. }];
  423. CFRunLoopRun();
  424. [realm beginWriteTransaction];
  425. [realm deleteObjects:[StringObject objectsInRealm:realm where:@"stringCol = 'a'"]];
  426. [realm commitWriteTransaction];
  427. [self startMeasuring];
  428. CFRunLoopRun();
  429. [token invalidate];
  430. }];
  431. }
  432. - (void)testCommitWriteTransactionWithListNotification {
  433. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  434. RLMRealm *realm = [self getStringObjects:5];
  435. [realm beginWriteTransaction];
  436. ArrayPropertyObject *arrayObj = [ArrayPropertyObject createInRealm:realm withValue:@[@"", [StringObject allObjectsInRealm:realm], @[]]];
  437. [realm commitWriteTransaction];
  438. id token = [arrayObj.array addNotificationBlock:^(__unused RLMArray *results, __unused RLMCollectionChange *change, __unused NSError *error) {
  439. CFRunLoopStop(CFRunLoopGetCurrent());
  440. }];
  441. CFRunLoopRun();
  442. [realm beginWriteTransaction];
  443. [realm deleteObjects:[StringObject objectsInRealm:realm where:@"stringCol = 'a'"]];
  444. [realm commitWriteTransaction];
  445. [self startMeasuring];
  446. CFRunLoopRun();
  447. [token invalidate];
  448. }];
  449. }
  450. - (void)testCrossThreadSyncLatency {
  451. const int stopValue = 500;
  452. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  453. RLMRealm *realm = self.testRealm;
  454. [realm beginWriteTransaction];
  455. [realm deleteAllObjects];
  456. IntObject *obj = [IntObject createInRealm:realm withValue:@[@0]];
  457. [realm commitWriteTransaction];
  458. dispatch_semaphore_t sema = dispatch_semaphore_create(0);
  459. [self dispatchAsync:^{
  460. RLMRealm *realm = self.testRealm;
  461. IntObject *obj = [[IntObject allObjectsInRealm:realm] firstObject];
  462. __block RLMNotificationToken *token;
  463. CFRunLoopPerformBlock(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, ^{
  464. token = [realm addNotificationBlock:^(__unused NSString *note, __unused RLMRealm *realm) {
  465. if (obj.intCol == stopValue) {
  466. CFRunLoopStop(CFRunLoopGetCurrent());
  467. }
  468. else if (obj.intCol % 2 == 0) {
  469. [realm transactionWithBlock:^{
  470. obj.intCol++;
  471. }];
  472. }
  473. }];
  474. dispatch_semaphore_signal(sema);
  475. });
  476. CFRunLoopRun();
  477. [token invalidate];
  478. }];
  479. RLMNotificationToken *token = [realm addNotificationBlock:^(__unused NSString *note, __unused RLMRealm *realm) {
  480. if (obj.intCol % 2 == 1 && obj.intCol < stopValue) {
  481. [realm transactionWithBlock:^{
  482. obj.intCol++;
  483. }];
  484. }
  485. }];
  486. dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
  487. [self startMeasuring];
  488. [realm transactionWithBlock:^{
  489. obj.intCol++;
  490. }];
  491. while (obj.intCol < stopValue) {
  492. [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
  493. }
  494. [self dispatchAsyncAndWait:^{}];
  495. [self stopMeasuring];
  496. [token invalidate];
  497. }];
  498. }
  499. - (void)testArrayKVOIndexHandlingRemoveForward {
  500. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  501. RLMRealm *realm = [self getStringObjects:50];
  502. [realm beginWriteTransaction];
  503. ArrayPropertyObject *obj = [ArrayPropertyObject createInRealm:realm withValue:@[@"", [StringObject allObjectsInRealm:realm], @[]]];
  504. [realm commitWriteTransaction];
  505. const NSUInteger initial = obj.array.count;
  506. [self observeObject:obj keyPath:@"array"
  507. until:^(id obj) { return [obj array].count < initial; }];
  508. [self startMeasuring];
  509. [realm beginWriteTransaction];
  510. for (NSUInteger i = 0; i < obj.array.count; i += 10) {
  511. [obj.array removeObjectAtIndex:i];
  512. }
  513. [realm commitWriteTransaction];
  514. dispatch_sync(_queue, ^{});
  515. }];
  516. }
  517. - (void)testArrayKVOIndexHandlingRemoveBackwards {
  518. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  519. RLMRealm *realm = [self getStringObjects:50];
  520. [realm beginWriteTransaction];
  521. ArrayPropertyObject *obj = [ArrayPropertyObject createInRealm:realm withValue:@[@"", [StringObject allObjectsInRealm:realm], @[]]];
  522. [realm commitWriteTransaction];
  523. const NSUInteger initial = obj.array.count;
  524. [self observeObject:obj keyPath:@"array"
  525. until:^(id obj) { return [obj array].count < initial; }];
  526. [self startMeasuring];
  527. [realm beginWriteTransaction];
  528. for (NSUInteger i = obj.array.count; i > 0; i -= i > 10 ? 10 : i) {
  529. [obj.array removeObjectAtIndex:i - 1];
  530. }
  531. [realm commitWriteTransaction];
  532. dispatch_sync(_queue, ^{});
  533. }];
  534. }
  535. - (void)testArrayKVOIndexHandlingInsertCompact {
  536. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  537. RLMRealm *realm = [self getStringObjects:50];
  538. [realm beginWriteTransaction];
  539. ArrayPropertyObject *obj = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
  540. [realm commitWriteTransaction];
  541. const NSUInteger count = [StringObject allObjectsInRealm:realm].count / 8;
  542. const NSUInteger factor = count / 10;
  543. [self observeObject:obj keyPath:@"array"
  544. until:^(id obj) { return [obj array].count >= count; }];
  545. RLMArray *array = obj.array;
  546. [self startMeasuring];
  547. [realm beginWriteTransaction];
  548. for (StringObject *so in [StringObject allObjectsInRealm:realm]) {
  549. [array addObject:so];
  550. if (array.count % factor == 0) {
  551. [realm commitWriteTransaction];
  552. dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER);
  553. [realm beginWriteTransaction];
  554. }
  555. if (array.count > count) {
  556. break;
  557. }
  558. }
  559. [realm commitWriteTransaction];
  560. dispatch_sync(_queue, ^{});
  561. }];
  562. }
  563. - (void)testArrayKVOIndexHandlingInsertSparse {
  564. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  565. RLMRealm *realm = [self getStringObjects:50];
  566. [realm beginWriteTransaction];
  567. ArrayPropertyObject *obj = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
  568. [realm commitWriteTransaction];
  569. const NSUInteger count = [StringObject allObjectsInRealm:realm].count / 8;
  570. const NSUInteger factor = count / 10;
  571. [self observeObject:obj keyPath:@"array"
  572. until:^(id obj) { return [obj array].count >= count; }];
  573. RLMArray *array = obj.array;
  574. [self startMeasuring];
  575. [realm beginWriteTransaction];
  576. for (StringObject *so in [StringObject allObjectsInRealm:realm]) {
  577. NSUInteger index = array.count;
  578. if (array.count > factor) {
  579. index = index * 3 % factor;
  580. }
  581. [array insertObject:so atIndex:index];
  582. if (array.count % factor == 0) {
  583. [realm commitWriteTransaction];
  584. dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER);
  585. [realm beginWriteTransaction];
  586. }
  587. if (array.count > count) {
  588. break;
  589. }
  590. }
  591. [realm commitWriteTransaction];
  592. dispatch_sync(_queue, ^{});
  593. }];
  594. }
  595. - (void)observeObject:(RLMObject *)object keyPath:(NSString *)keyPath until:(int (^)(id))block {
  596. self.sema = dispatch_semaphore_create(0);
  597. self.queue = dispatch_queue_create("bg", 0);
  598. RLMRealmConfiguration *config = object.realm.configuration;
  599. NSString *className = [object.class className];
  600. dispatch_async(_queue, ^{
  601. RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
  602. id obj = [[realm allObjects:className] firstObject];
  603. [obj addObserver:self forKeyPath:keyPath options:(NSKeyValueObservingOptions)0 context:(__bridge void *)_sema];
  604. dispatch_semaphore_signal(_sema);
  605. while (!block(obj)) {
  606. [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
  607. }
  608. [obj removeObserver:self forKeyPath:keyPath context:(__bridge void *)_sema];
  609. });
  610. dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER);
  611. }
  612. - (void)observeValueForKeyPath:(__unused NSString *)keyPath
  613. ofObject:(__unused id)object
  614. change:(__unused NSDictionary *)change
  615. context:(void *)context {
  616. dispatch_semaphore_signal((__bridge dispatch_semaphore_t)context);
  617. }
  618. @end
  619. #endif