Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
212 views
in Technique[技术] by (71.8m points)

ios - Realm accessed from incorrect thread

I'm using serial GCD queue to work with realm. Application crashes with Realm accessed from incorrect thread exception when GCD starts to switch threads for the queue. Is there any way to bind given realm with a thread using GCD API?

Here's a quick example

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++;
    });
}];
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

From Realm docs: RLMRealm objects are not thread safe and cannot be shared across threads, so you must get an RLMRealm instance in each thread/dispatch_queue in which you want to read or write.

Also from Realm docs: RLMRealm objects are cached internally by Realm, and calling this method multiple times on a single thread within a single iteration of the run loop will normally return the same RLMRealm object.

So knowing this, I modified your code sample to get the RLMRealm directly from the dispatch_async block where it is used, without incurring a performance penalty, since it is cached.

I also noticed that an AccelerationEvent was passed across threads, which is also not allowed. So this modified code sample passes NSStrings across threads instead.

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

I haven't run this code to confirm that it works, so let me know if this still doesn't resolve the issue.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...