【问题标题】:Realm accessed from incorrect thread从不正确的线程访问的领域
【发布时间】:2014-09-04 08:45:12
【问题描述】:

我正在使用串行 GCD 队列来处理领域。当 GCD 开始为队列切换线程时,应用程序崩溃并出现 Realm accessed from incorrect thread 异常。有没有办法使用 GCD API 将给定领域与线程绑定?

这是一个简单的例子

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++;
    });
}];

【问题讨论】:

    标签: ios objective-c cocoa realm


    【解决方案1】:

    From Realm docsRLMRealm 对象不是线程安全的,不能跨线程共享,因此您必须在要读取或写入的每个线程/dispatch_queue 中获得一个 RLMRealm 实例。

    Also from Realm docsRLMRealm 对象由 Realm 内部缓存,在运行循环的一次迭代中在单个线程上多次调用此方法通常会返回相同的 RLMRealm 对象。

    知道了这一点,我修改了您的代码示例,直接从使用它的dispatch_async 块中获取RLMRealm,而不会导致性能损失,因为它已被缓存。

    我还注意到 AccelerationEvent 跨线程传递,这也是不允许的。所以这个修改后的代码示例改为跨线程传递NSStrings。

    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++;
        });
    }];
    

    我还没有运行此代码来确认它是否有效,所以如果这仍然不能解决问题,请告诉我。

    【讨论】:

    • @jpsim 从你说的看,我认为只有 RLMRealm 不是线程安全的,事实证明从该领域得到的所有 RLMObject 子类也不是线程安全的
    • 来自 Realm 的文档:“你只能在创建它的线程上使用一个对象,并且你不能直接访问它的 ivars 以获取任何持久属性。” realm.io/docs/objc/latest/#models
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-06
    • 2017-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-04
    • 1970-01-01
    相关资源
    最近更新 更多