从错误的线程访问Realm。

18

我正在使用串行GCD队列与Realm进行交互。当GCD开始在队列中切换线程时,应用程序会崩溃并提示“从错误的线程访问Realm”的异常。是否有办法使用GCD API将给定的realm绑定到线程上?

以下是一个快速示例:

self.realmQueue = dispatch_queue_create("db", DISPATCH_QUEUE_SERIAL);

__block RLMRealm *realm = nil;
dispatch_async(self.realmQueue, ^{
    realm = [RLMRealm realmWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"temp"]];
});

self.motionManager = [[CMMotionManager alloc] init];
self.motionManager.accelerometerUpdateInterval = 0.001;
__block int i = 0;
__block BOOL shouldBeginWriteTransaction = YES;

[self.motionManager startAccelerometerUpdatesToQueue:[[NSOperationQueue alloc] init] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {

    dispatch_async(self.realmQueue, ^{
        if (shouldBeginWriteTransaction) {
            [realm beginWriteTransaction];
            shouldBeginWriteTransaction = NO;
        }

        AccelerationEvent *event = [[AccelerationEvent alloc] init];
        event.x = accelerometerData.acceleration.x;
        event.y = accelerometerData.acceleration.x;
        event.z = accelerometerData.acceleration.y;
        event.time = [NSDate date];
        [realm addObject:event];

        if (i % 1000) {
            dispatch_async(dispatch_get_main_queue(), ^{
                self.xLabel.text = [NSString stringWithFormat:@"%f", event.x];
                self.yLabel.text = [NSString stringWithFormat:@"%f", event.y];
                self.zLabel.text = [NSString stringWithFormat:@"%f", event.z];
            });
        }

        if (i % 10000 == 0) {
            NSDate *startDate = [NSDate date];
            [realm commitWriteTransaction];
            NSLog(@"save time: %f", [[NSDate date] timeIntervalSinceDate:startDate]);
            shouldBeginWriteTransaction = YES;
        }

        i++;
    });
}];
1个回答

35

来自 Realm 文档:RLMRealm 对象不是线程安全的,不能在多个线程之间共享,因此您必须在每个要读取或写入的线程/dispatch_queue 中获取一个 RLMRealm 实例。

同样来自 Realm 文档:RLMRealm 对象在 Realm 内部被缓存,因此在单个迭代的运行循环内,在同一线程上多次调用此方法通常会返回相同的 RLMRealm 对象。

基于这些知识,我修改了您的代码示例,直接从使用它的 dispatch_async 块中获取 RLMRealm,而无需承担性能损失,因为它已经被缓存了。

我还注意到加速度事件(AccelerationEvent)被跨线程传递,这也是不允许的。因此,这个修改后的代码示例传递了 NSString。

self.realmQueue = dispatch_queue_create("db", DISPATCH_QUEUE_SERIAL);

self.motionManager = [[CMMotionManager alloc] init];
self.motionManager.accelerometerUpdateInterval = 0.001;
__block int i = 0;
__block BOOL shouldBeginWriteTransaction = YES;

[self.motionManager startAccelerometerUpdatesToQueue:[[NSOperationQueue alloc] init] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {

    dispatch_async(self.realmQueue, ^{
        RLMRealm *realm = [RLMRealm realmWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"temp"]];
        if (shouldBeginWriteTransaction) {
            [realm beginWriteTransaction];
            shouldBeginWriteTransaction = NO;
        }

        AccelerationEvent *event = [[AccelerationEvent alloc] init];
        event.x = accelerometerData.acceleration.x;
        event.y = accelerometerData.acceleration.x;
        event.z = accelerometerData.acceleration.y;
        event.time = [NSDate date];
        [realm addObject:event];

        if (i % 1000) {
            NSString *xString = [NSString stringWithFormat:@"%f", event.x];
            NSString *yString = [NSString stringWithFormat:@"%f", event.y];
            NSString *zString = [NSString stringWithFormat:@"%f", event.z];
            dispatch_async(dispatch_get_main_queue(), ^{
                self.xLabel.text = xString;
                self.yLabel.text = yString;
                self.zLabel.text = zString;
            });
        }

        if (i % 10000 == 0) {
            NSDate *startDate = [NSDate date];
            [realm commitWriteTransaction];
            NSLog(@"save time: %f", [[NSDate date] timeIntervalSinceDate:startDate]);
            shouldBeginWriteTransaction = YES;
        }

        i++;
    });
}];

我还没有运行这段代码来确认它是否有效,如果问题仍未解决,请告诉我。


根据你所说的,我认为只有 RLMRealm 不是线程安全的,但事实证明从该 Realm 获取的所有 RLMObject 子类也不是线程安全的。 - onmyway133
1
从 Realm 的文档中可以得知:“你只能在创建它的线程上使用对象,并且不能直接访问任何持久化属性的实例变量。” https://realm.io/docs/objc/latest/#models - jpsim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接