2014-09-04 7 views
17

Ich verwende serielle GCD-Warteschlange, um mit Realm zu arbeiten. Anwendung stürzt mit Ausnahme Realm accessed from incorrect thread ab, wenn GCD beginnt, Threads für die Warteschlange zu wechseln. Gibt es eine Möglichkeit, ein bestimmtes Realm mithilfe der GCD-API mit einem Thread zu verknüpfen?Realm von falschem Thread zugegriffen

Hier ist ein kurzes Beispiel

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

Antwort

34

From Realm docs: RLMRealm Objekte sind nicht Thread-sicher und nicht über Threads gemeinsam genutzt werden können, so dass Sie eine RLMRealm Instanz in jedem Thread/dispatch_queue, in dem Sie erhalten müssen lesen oder schreiben .

Also from Realm docs: RLMRealm Objekte werden intern von Realm, zwischengespeichert und dieses Verfahren mehrmals auf einem einzelnen Thread innerhalb einer einzelnen Iteration der Laufschleife ruft in der Regel die gleiche RLMRealm Objekt zurückgeben.

Also, dies zu wissen, änderte ich Ihr Codebeispiel, um die RLMRealm direkt aus dem dispatch_async-Block, wo es verwendet wird, ohne eine Leistungseinbußen, da es im Cache gespeichert ist.

Ich habe auch festgestellt, dass ein AccelerationEvent über Threads übergeben wurde, was auch nicht erlaubt ist. Dieses modifizierte Codebeispiel übergibt stattdessen NSString s über Threads.

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

Ich habe diesen Code nicht ausgeführt, um zu bestätigen, dass es funktioniert, also lassen Sie mich wissen, ob dies das Problem immer noch nicht löst.

+0

Danke, es hat funktioniert. – Maxim

+0

@jpsim von dem, was Sie gesagt haben, dachte ich, nur RLMRealm ist nicht threadsicher, es stellt sich heraus, dass alle RLMObject-Unterklassen aus diesem Bereich nicht threadsicher sind, auch – onmyway133

+1

Aus der Dokumentation von Realm: "Sie können nur ein Objekt auf dem verwenden Thread, von dem es erstellt wurde, und Sie können nicht direkt auf seine Ivars für alle beibehaltenen Eigenschaften zugreifen. " https://realm.io/docs/objc/latest/#models – jpsim

Verwandte Themen