PerformanceTests.m 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  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)testEnumerateAndAccessAllTV {
  154. RLMRealm *realm = [self getStringObjects:50];
  155. [self measureBlock:^{
  156. [realm beginWriteTransaction];
  157. for (StringObject *so in [StringObject allObjectsInRealm:realm]) {
  158. (void)[so stringCol];
  159. }
  160. [realm cancelWriteTransaction];
  161. }];
  162. }
  163. - (void)testEnumerateAndAccessAllSlow {
  164. RLMRealm *realm = [self getStringObjects:5];
  165. [self measureBlock:^{
  166. RLMResults *all = [StringObject allObjectsInRealm:realm];
  167. for (NSUInteger i = 0; i < all.count; ++i) {
  168. (void)[all[i] stringCol];
  169. }
  170. }];
  171. }
  172. - (void)testEnumerateAndAccessArrayProperty {
  173. RLMRealm *realm = [self getStringObjects:5];
  174. [realm beginWriteTransaction];
  175. ArrayPropertyObject *apo = [ArrayPropertyObject createInRealm:realm
  176. withValue:@[@"name", [StringObject allObjectsInRealm:realm], @[]]];
  177. [realm commitWriteTransaction];
  178. [self measureBlock:^{
  179. for (StringObject *so in apo.array) {
  180. (void)[so stringCol];
  181. }
  182. }];
  183. }
  184. - (void)testEnumerateAndAccessArrayPropertySlow {
  185. RLMRealm *realm = [self getStringObjects:5];
  186. [realm beginWriteTransaction];
  187. ArrayPropertyObject *apo = [ArrayPropertyObject createInRealm:realm
  188. withValue:@[@"name", [StringObject allObjectsInRealm:realm], @[]]];
  189. [realm commitWriteTransaction];
  190. [self measureBlock:^{
  191. RLMArray *array = apo.array;
  192. for (NSUInteger i = 0; i < array.count; ++i) {
  193. (void)[array[i] stringCol];
  194. }
  195. }];
  196. }
  197. - (void)testEnumerateAndMutateAll {
  198. RLMRealm *realm = [self getStringObjects:5];
  199. [self measureBlock:^{
  200. [realm beginWriteTransaction];
  201. for (StringObject *so in [StringObject allObjectsInRealm:realm]) {
  202. so.stringCol = @"c";
  203. }
  204. [realm commitWriteTransaction];
  205. }];
  206. }
  207. - (void)testEnumerateAndMutateQuery {
  208. RLMRealm *realm = [self getStringObjects:1];
  209. [self measureBlock:^{
  210. [realm beginWriteTransaction];
  211. for (StringObject *so in [StringObject objectsInRealm:realm where:@"stringCol != 'b'"]) {
  212. so.stringCol = @"c";
  213. }
  214. [realm commitWriteTransaction];
  215. }];
  216. }
  217. - (void)testQueryConstruction {
  218. RLMRealm *realm = self.realmWithTestPath;
  219. 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'}"];
  220. [self measureBlock:^{
  221. for (int i = 0; i < 500; ++i) {
  222. [AllTypesObject objectsInRealm:realm withPredicate:predicate];
  223. }
  224. }];
  225. }
  226. - (void)testDeleteAll {
  227. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  228. RLMRealm *realm = [self getStringObjects:50];
  229. [self startMeasuring];
  230. [realm beginWriteTransaction];
  231. [realm deleteObjects:[StringObject allObjectsInRealm:realm]];
  232. [realm commitWriteTransaction];
  233. [self stopMeasuring];
  234. }];
  235. }
  236. - (void)testQueryDeletion {
  237. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  238. RLMRealm *realm = [self getStringObjects:5];
  239. [self startMeasuring];
  240. [realm beginWriteTransaction];
  241. [realm deleteObjects:[StringObject objectsInRealm:realm where:@"stringCol = 'a' OR stringCol = 'b'"]];
  242. [realm commitWriteTransaction];
  243. [self stopMeasuring];
  244. }];
  245. }
  246. - (void)testManualDeletion {
  247. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  248. RLMRealm *realm = [self getStringObjects:5];
  249. NSMutableArray *objects = [NSMutableArray arrayWithCapacity:10000];
  250. for (StringObject *obj in [StringObject allObjectsInRealm:realm]) {
  251. [objects addObject:obj];
  252. }
  253. [self startMeasuring];
  254. [realm beginWriteTransaction];
  255. [realm deleteObjects:objects];
  256. [realm commitWriteTransaction];
  257. [self stopMeasuring];
  258. }];
  259. }
  260. - (void)testUnIndexedStringLookup {
  261. RLMRealm *realm = self.realmWithTestPath;
  262. [realm beginWriteTransaction];
  263. for (int i = 0; i < 1000; ++i) {
  264. [StringObject createInRealm:realm withValue:@[@(i).stringValue]];
  265. }
  266. [realm commitWriteTransaction];
  267. [self measureBlock:^{
  268. for (int i = 0; i < 1000; ++i) {
  269. [[StringObject objectsInRealm:realm where:@"stringCol = %@", @(i).stringValue] firstObject];
  270. }
  271. }];
  272. }
  273. - (void)testIndexedStringLookup {
  274. RLMRealm *realm = self.realmWithTestPath;
  275. [realm beginWriteTransaction];
  276. for (int i = 0; i < 1000; ++i) {
  277. [IndexedStringObject createInRealm:realm withValue:@[@(i).stringValue]];
  278. }
  279. [realm commitWriteTransaction];
  280. [self measureBlock:^{
  281. for (int i = 0; i < 1000; ++i) {
  282. [[IndexedStringObject objectsInRealm:realm where:@"stringCol = %@", @(i).stringValue] firstObject];
  283. }
  284. }];
  285. }
  286. - (void)testLargeINQuery {
  287. RLMRealm *realm = self.realmWithTestPath;
  288. [realm beginWriteTransaction];
  289. NSMutableArray *ids = [NSMutableArray arrayWithCapacity:3000];
  290. for (int i = 0; i < 3000; ++i) {
  291. [IntObject createInRealm:realm withValue:@[@(i)]];
  292. if (i % 2) {
  293. [ids addObject:@(i)];
  294. }
  295. }
  296. [realm commitWriteTransaction];
  297. [self measureBlock:^{
  298. (void)[[IntObject objectsInRealm:realm where:@"intCol IN %@", ids] firstObject];
  299. }];
  300. }
  301. - (void)testSortingAllObjects {
  302. RLMRealm *realm = self.realmWithTestPath;
  303. [realm beginWriteTransaction];
  304. for (int i = 0; i < 3000; ++i) {
  305. [IntObject createInRealm:realm withValue:@[@(arc4random())]];
  306. }
  307. [realm commitWriteTransaction];
  308. [self measureBlock:^{
  309. (void)[[IntObject allObjectsInRealm:realm] sortedResultsUsingProperty:@"intCol" ascending:YES].lastObject;
  310. }];
  311. }
  312. - (void)testRealmCreationCached {
  313. __block RLMRealm *realm;
  314. [self dispatchAsyncAndWait:^{
  315. realm = [self realmWithTestPath]; // ensure a cached realm for the path
  316. }];
  317. [self measureBlock:^{
  318. for (int i = 0; i < 250; ++i) {
  319. @autoreleasepool {
  320. [self realmWithTestPath];
  321. }
  322. }
  323. }];
  324. [realm configuration];
  325. }
  326. - (void)testRealmCreationUncached {
  327. [self measureBlock:^{
  328. for (int i = 0; i < 50; ++i) {
  329. @autoreleasepool {
  330. [self realmWithTestPath];
  331. }
  332. }
  333. }];
  334. }
  335. - (void)testRealmFileCreation {
  336. RLMRealmConfiguration *config = [RLMRealmConfiguration new];
  337. __block int measurement = 0;
  338. const int iterations = 10;
  339. [self measureBlock:^{
  340. for (int i = 0; i < iterations; ++i) {
  341. @autoreleasepool {
  342. config.inMemoryIdentifier = @(measurement * iterations + i).stringValue;
  343. [RLMRealm realmWithConfiguration:config error:nil];
  344. }
  345. }
  346. ++measurement;
  347. }];
  348. }
  349. - (void)testInvalidateRefresh {
  350. RLMRealm *realm = [self testRealm];
  351. [self measureBlock:^{
  352. for (int i = 0; i < 50000; ++i) {
  353. @autoreleasepool {
  354. [realm invalidate];
  355. [realm refresh];
  356. }
  357. }
  358. }];
  359. }
  360. - (void)testCommitWriteTransaction {
  361. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  362. RLMRealm *realm = self.testRealm;
  363. [realm beginWriteTransaction];
  364. IntObject *obj = [IntObject createInRealm:realm withValue:@[@0]];
  365. [realm commitWriteTransaction];
  366. [self startMeasuring];
  367. while (obj.intCol < 100) {
  368. [realm transactionWithBlock:^{
  369. obj.intCol++;
  370. }];
  371. }
  372. [self stopMeasuring];
  373. }];
  374. }
  375. - (void)testCommitWriteTransactionWithLocalNotification {
  376. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  377. RLMRealm *realm = self.testRealm;
  378. [realm beginWriteTransaction];
  379. IntObject *obj = [IntObject createInRealm:realm withValue:@[@0]];
  380. [realm commitWriteTransaction];
  381. RLMNotificationToken *token = [realm addNotificationBlock:^(__unused NSString *note, __unused RLMRealm *realm) { }];
  382. [self startMeasuring];
  383. while (obj.intCol < 500) {
  384. [realm transactionWithBlock:^{
  385. obj.intCol++;
  386. }];
  387. }
  388. [self stopMeasuring];
  389. [token invalidate];
  390. }];
  391. }
  392. - (void)testCommitWriteTransactionWithCrossThreadNotification {
  393. const int stopValue = 500;
  394. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  395. RLMRealm *realm = self.testRealm;
  396. [realm beginWriteTransaction];
  397. IntObject *obj = [IntObject createInRealm:realm withValue:@[@0]];
  398. [realm commitWriteTransaction];
  399. dispatch_semaphore_t sema = dispatch_semaphore_create(0);
  400. [self dispatchAsync:^{
  401. RLMRealm *realm = self.testRealm;
  402. IntObject *obj = [[IntObject allObjectsInRealm:realm] firstObject];
  403. __block RLMNotificationToken *token;
  404. CFRunLoopPerformBlock(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, ^{
  405. token = [realm addNotificationBlock:^(__unused NSString *note, __unused RLMRealm *realm) {
  406. if (obj.intCol == stopValue) {
  407. CFRunLoopStop(CFRunLoopGetCurrent());
  408. }
  409. }];
  410. dispatch_semaphore_signal(sema);
  411. });
  412. CFRunLoopRun();
  413. [token invalidate];
  414. }];
  415. dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
  416. [self startMeasuring];
  417. while (obj.intCol < stopValue) {
  418. [realm transactionWithBlock:^{
  419. obj.intCol++;
  420. }];
  421. }
  422. [self dispatchAsyncAndWait:^{}];
  423. [self stopMeasuring];
  424. }];
  425. }
  426. - (void)testCommitWriteTransactionWithResultsNotification {
  427. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  428. RLMRealm *realm = [self getStringObjects:5];
  429. RLMResults *results = [StringObject allObjectsInRealm:realm];
  430. id token = [results addNotificationBlock:^(__unused RLMResults *results, __unused RLMCollectionChange *change, __unused NSError *error) {
  431. CFRunLoopStop(CFRunLoopGetCurrent());
  432. }];
  433. CFRunLoopRun();
  434. [realm beginWriteTransaction];
  435. [realm deleteObjects:[StringObject objectsInRealm:realm where:@"stringCol = 'a'"]];
  436. [realm commitWriteTransaction];
  437. [self startMeasuring];
  438. CFRunLoopRun();
  439. [token invalidate];
  440. }];
  441. }
  442. - (void)testCommitWriteTransactionWithListNotification {
  443. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  444. RLMRealm *realm = [self getStringObjects:5];
  445. [realm beginWriteTransaction];
  446. ArrayPropertyObject *arrayObj = [ArrayPropertyObject createInRealm:realm withValue:@[@"", [StringObject allObjectsInRealm:realm], @[]]];
  447. [realm commitWriteTransaction];
  448. id token = [arrayObj.array addNotificationBlock:^(__unused RLMArray *results, __unused RLMCollectionChange *change, __unused NSError *error) {
  449. CFRunLoopStop(CFRunLoopGetCurrent());
  450. }];
  451. CFRunLoopRun();
  452. [realm beginWriteTransaction];
  453. [realm deleteObjects:[StringObject objectsInRealm:realm where:@"stringCol = 'a'"]];
  454. [realm commitWriteTransaction];
  455. [self startMeasuring];
  456. CFRunLoopRun();
  457. [token invalidate];
  458. }];
  459. }
  460. - (void)testCrossThreadSyncLatency {
  461. const int stopValue = 500;
  462. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  463. RLMRealm *realm = self.testRealm;
  464. [realm beginWriteTransaction];
  465. [realm deleteAllObjects];
  466. IntObject *obj = [IntObject createInRealm:realm withValue:@[@0]];
  467. [realm commitWriteTransaction];
  468. dispatch_semaphore_t sema = dispatch_semaphore_create(0);
  469. [self dispatchAsync:^{
  470. RLMRealm *realm = self.testRealm;
  471. IntObject *obj = [[IntObject allObjectsInRealm:realm] firstObject];
  472. __block RLMNotificationToken *token;
  473. CFRunLoopPerformBlock(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, ^{
  474. token = [realm addNotificationBlock:^(__unused NSString *note, __unused RLMRealm *realm) {
  475. if (obj.intCol == stopValue) {
  476. CFRunLoopStop(CFRunLoopGetCurrent());
  477. }
  478. else if (obj.intCol % 2 == 0) {
  479. [realm transactionWithBlock:^{
  480. obj.intCol++;
  481. }];
  482. }
  483. }];
  484. dispatch_semaphore_signal(sema);
  485. });
  486. CFRunLoopRun();
  487. [token invalidate];
  488. }];
  489. RLMNotificationToken *token = [realm addNotificationBlock:^(__unused NSString *note, __unused RLMRealm *realm) {
  490. if (obj.intCol % 2 == 1 && obj.intCol < stopValue) {
  491. [realm transactionWithBlock:^{
  492. obj.intCol++;
  493. }];
  494. }
  495. }];
  496. dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
  497. [self startMeasuring];
  498. [realm transactionWithBlock:^{
  499. obj.intCol++;
  500. }];
  501. while (obj.intCol < stopValue) {
  502. [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
  503. }
  504. [self dispatchAsyncAndWait:^{}];
  505. [self stopMeasuring];
  506. [token invalidate];
  507. }];
  508. }
  509. - (void)testArrayKVOIndexHandlingRemoveForward {
  510. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  511. RLMRealm *realm = [self getStringObjects:50];
  512. [realm beginWriteTransaction];
  513. ArrayPropertyObject *obj = [ArrayPropertyObject createInRealm:realm withValue:@[@"", [StringObject allObjectsInRealm:realm], @[]]];
  514. [realm commitWriteTransaction];
  515. const NSUInteger initial = obj.array.count;
  516. [self observeObject:obj keyPath:@"array"
  517. until:^(ArrayPropertyObject *obj) { return obj.array.count < initial; }];
  518. [self startMeasuring];
  519. [realm beginWriteTransaction];
  520. for (NSUInteger i = 0; i < obj.array.count; i += 10) {
  521. [obj.array removeObjectAtIndex:i];
  522. }
  523. [realm commitWriteTransaction];
  524. dispatch_sync(_queue, ^{});
  525. }];
  526. }
  527. - (void)testArrayKVOIndexHandlingRemoveBackwards {
  528. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  529. RLMRealm *realm = [self getStringObjects:50];
  530. [realm beginWriteTransaction];
  531. ArrayPropertyObject *obj = [ArrayPropertyObject createInRealm:realm withValue:@[@"", [StringObject allObjectsInRealm:realm], @[]]];
  532. [realm commitWriteTransaction];
  533. const NSUInteger initial = obj.array.count;
  534. [self observeObject:obj keyPath:@"array"
  535. until:^(ArrayPropertyObject *obj) { return obj.array.count < initial; }];
  536. [self startMeasuring];
  537. [realm beginWriteTransaction];
  538. for (NSUInteger i = obj.array.count; i > 0; i -= i > 10 ? 10 : i) {
  539. [obj.array removeObjectAtIndex:i - 1];
  540. }
  541. [realm commitWriteTransaction];
  542. dispatch_sync(_queue, ^{});
  543. }];
  544. }
  545. - (void)testArrayKVOIndexHandlingInsertCompact {
  546. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  547. RLMRealm *realm = [self getStringObjects:50];
  548. [realm beginWriteTransaction];
  549. ArrayPropertyObject *obj = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
  550. [realm commitWriteTransaction];
  551. const NSUInteger count = [StringObject allObjectsInRealm:realm].count / 8;
  552. const NSUInteger factor = count / 10;
  553. [self observeObject:obj keyPath:@"array"
  554. until:^(ArrayPropertyObject *obj) { return obj.array.count >= count; }];
  555. RLMArray *array = obj.array;
  556. [self startMeasuring];
  557. [realm beginWriteTransaction];
  558. for (StringObject *so in [StringObject allObjectsInRealm:realm]) {
  559. [array addObject:so];
  560. if (array.count % factor == 0) {
  561. [realm commitWriteTransaction];
  562. dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER);
  563. [realm beginWriteTransaction];
  564. }
  565. if (array.count > count) {
  566. break;
  567. }
  568. }
  569. [realm commitWriteTransaction];
  570. dispatch_sync(_queue, ^{});
  571. }];
  572. }
  573. - (void)testArrayKVOIndexHandlingInsertSparse {
  574. [self measureMetrics:self.class.defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
  575. RLMRealm *realm = [self getStringObjects:50];
  576. [realm beginWriteTransaction];
  577. ArrayPropertyObject *obj = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
  578. [realm commitWriteTransaction];
  579. const NSUInteger count = [StringObject allObjectsInRealm:realm].count / 8;
  580. const NSUInteger factor = count / 10;
  581. [self observeObject:obj keyPath:@"array"
  582. until:^(id obj) { return [obj array].count >= count; }];
  583. RLMArray *array = obj.array;
  584. [self startMeasuring];
  585. [realm beginWriteTransaction];
  586. for (StringObject *so in [StringObject allObjectsInRealm:realm]) {
  587. NSUInteger index = array.count;
  588. if (array.count > factor) {
  589. index = index * 3 % factor;
  590. }
  591. [array insertObject:so atIndex:index];
  592. if (array.count % factor == 0) {
  593. [realm commitWriteTransaction];
  594. dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER);
  595. [realm beginWriteTransaction];
  596. }
  597. if (array.count > count) {
  598. break;
  599. }
  600. }
  601. [realm commitWriteTransaction];
  602. dispatch_sync(_queue, ^{});
  603. }];
  604. }
  605. - (void)observeObject:(RLMObject *)object keyPath:(NSString *)keyPath until:(int (^)(id))block {
  606. self.sema = dispatch_semaphore_create(0);
  607. self.queue = dispatch_queue_create("bg", 0);
  608. RLMRealmConfiguration *config = object.realm.configuration;
  609. NSString *className = [object.class className];
  610. dispatch_async(_queue, ^{
  611. RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
  612. id obj = [[realm allObjects:className] firstObject];
  613. [obj addObserver:self forKeyPath:keyPath options:(NSKeyValueObservingOptions)0 context:(__bridge void *)_sema];
  614. dispatch_semaphore_signal(_sema);
  615. while (!block(obj)) {
  616. [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
  617. }
  618. [obj removeObserver:self forKeyPath:keyPath context:(__bridge void *)_sema];
  619. });
  620. dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER);
  621. }
  622. - (void)observeValueForKeyPath:(__unused NSString *)keyPath
  623. ofObject:(__unused id)object
  624. change:(__unused NSDictionary *)change
  625. context:(void *)context {
  626. dispatch_semaphore_signal((__bridge dispatch_semaphore_t)context);
  627. }
  628. @end
  629. #endif