Search code examples
objective-crealm

Deletion of single object gives invalid change notification


I notice strange thing in notification while deleting objects from Realm. When you delete single object sometimes change contains 2 deletions and 1 insertion. If you display results in table all looks fine. But i display results on map and this trigger not needed animation for reinserted object...

I found 2 ways to solve problem:

  • Manually filter reinserted object
  • Sort objects by primaryKey

Does query with sorting by primaryKey much slower? Or maybe there is a better solution ?

Here is some test code (Realm 2.3.0, Xcode 8.2.1):

@interface ModelA : RLMObject
@property int pk;
@end

@implementation ModelA
+ (NSString *) primaryKey
{
    return @"pk";
}
@end

@implementation ViewController
{
    RLMNotificationToken *_nfA;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    RLMRealm *realm = [RLMRealm defaultRealm];

    [realm beginWriteTransaction];
    for(int i = 0; i<10; ++i)
    {
        ModelA *a = [[ModelA alloc] init];
        a.pk = i;
        [realm addOrUpdateObject:a];
    }
    [realm commitWriteTransaction];

    _nfA = [[ModelA allObjects] addNotificationBlock:^(RLMResults *results, RLMCollectionChange *change, NSError *error)
    {
        if(change)
        {
            NSLog(@"Insertions: %lu deleteions:%lu modifications:%lu\n", change.insertions.count, change.deletions.count, change.modifications.count);
        }
    }];

    for(int i=0; i<10; ++i)
    {
        RLMRealm *realm = [RLMRealm defaultRealm];
        [realm beginWriteTransaction];
        [realm deleteObject:[[ModelA allObjects] objectAtIndex:0]];
        [realm commitWriteTransaction];
    }            
}
@end

Output:

2017-01-20 10:44:19.394 Test[7536:12871658] Insertions: 1 deleteions:2 modifications:0
2017-01-20 10:44:19.395 Test[7536:12871658] Insertions: 1 deleteions:2 modifications:0
2017-01-20 10:44:19.395 Test[7536:12871658] Insertions: 1 deleteions:2 modifications:0
2017-01-20 10:44:19.396 Test[7536:12871658] Insertions: 1 deleteions:2 modifications:0
2017-01-20 10:44:19.396 Test[7536:12871658] Insertions: 1 deleteions:2 modifications:0
2017-01-20 10:44:19.397 Test[7536:12871658] Insertions: 1 deleteions:2 modifications:0
2017-01-20 10:44:19.397 Test[7536:12871658] Insertions: 1 deleteions:2 modifications:0
2017-01-20 10:44:19.397 Test[7536:12871658] Insertions: 1 deleteions:2 modifications:0
2017-01-20 10:44:19.398 Test[7536:12871658] Insertions: 0 deleteions:1 modifications:0
2017-01-20 10:44:19.402 Test[7536:12871658] Insertions: 0 deleteions:1 modifications:0

Output looks fine if replace request line with something like this:

_nfA = [[[ModelA allObjects] sortedResultsUsingKeyPath:@"pk" ascending:NO] addNotificationBlock:...

Solution

  • The insertion/deletion APIs of UITableView are very finicky in that it's quite easy to trigger an inconsistency exception. The Realm change notifications API have been designed to ensure very tight integration with UITableView / UICollectionView's behavior, which is most likely why this is happening. I'm assuming that it's deleting an adjacent cell, and then re-inserting it as part of the transaction.

    There's no way to override Realm's behaviour like that. If it really is deleting and re-inserting the same cell, it should be possible for you to programmatically determine that row (By comparing their indices) and then ignore applying an animation to it.

    Any kind of sorting will be inherently slower than not sorting. But since you're sorting via an integer, it should still be faster than sorting via a string.