2014-09-04 66 views
17

我正在使用串行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++; 
    }); 
}]; 

回答

34

From Realm docsRLMRealm对象不是线程安全的,并且不能跨线程共享,所以你必须让每个线程/ dispatch_queue的RLMRealm比如在要读取或写入。

Also from Realm docsRLMRealm目的通过境界内部缓存,并在调用此方法在单个线程多次运行循环的单次迭代内将正常返回同一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++; 
    }); 
}]; 

我还没有运行此代码来确认它的工作原理,所以让我知道如果这仍然不能解决问题。

+0

谢谢,它的工作。 – Maxim 2014-09-05 11:38:01

+0

@jpsim从你所说的话,我认为只有RLMRealm不是线程安全的,事实证明,从该领域获得的所有RLMObject子类都不是线程安全的 – onmyway133 2015-07-21 17:04:43

+1

从Realm的文档:“你只能使用它创建的线程,并且你不能直接访问它的ivars以获得任何持久性属性。“ https://realm.io/docs/objc/latest/#models – jpsim 2015-07-21 21:14:11

相关问题