2014-09-04 29 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

相關問題