Search code examples
iosmultithreadingrealm

How to prevent realm access on incorrect thread


I have a model class that uses Realm to perform CRUD operations.

I am trying to perform all my operations on a serial queue.

I have declared the queue property:

@property (nonatomic, strong) dispatch_queue_t realmQueue;
@property (strong,nonatomic) RLMRealm *secureRealm;

and I used singleton to initialize the queue:

+ (id)sharedInstance {
    static ActivityManager *sharedManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedManager = [[self alloc] init];

    });

    return sharedManager;
}

-(id)init{
    self.realmQueue = dispatch_queue_create("realm_activity", DISPATCH_QUEUE_SERIAL);
    dispatch_async(self.realmQueue, ^{
    _secureRealm = [RLMRealm defaultRealm];
    });
    return self;
}

-(NSArray *)getDataOnSettingFilter{

    NSMutableArray *activityArr = [NSMutableArray array];
    NSString *filterSQL = //;

    RLMResults *activityResults = [[[self getActivitySource]activities] objectsWhere:filterSQL];

    dispatch_async(self.realmQueue, ^{
        [_secureRealm beginWriteTransaction];

        for (RealmActivity *activity in activityResults){

        //modify realm properties

        [_secureRealm addOrUpdateObject:activity];
        [activityArr addObject:activity];
    }
        [_secureRealm commitWriteTransaction];
    });
    return [activityArr copy];
}

and when I call the above method using:

  [[ActivityManager sharedInstance]getDataOnSettingFilter]

I get a crash saying realm is accessed from a incorrect thread.

What am I doing wrong here. How can I check if I am calling the realm on correct thread always ?


Solution

  • You shouldn't store a reference to a singleton Realm instance, since you cannot ensure that you will always access it from the same thread. As the Realm documentation states,

    Instances of RLMRealm, RLMResults, or RLMArray, or managed instances of RLMObject are thread-confined, meaning that they can only be used on the thread on which they were created, otherwise an exception is thrown

    meaning that you should never try to use RLMRealm *realm = [RLMRealm defaultRealm]; to create a singleton Realm instance and than try to access that everywhere from your app.

    You could try using RLMThreadSafeReference to create references to Realm objects that can be safely shared between threads, but as the Passing instances across threads part of the documentation states, these references can only be used once, so they can't be reused as a singleton.

    However, there is no need for such thing, since every time you call RLMRealm *realm = [RLMRealm defaultRealm];, the framework automatically creates a reference to your currently used Realm on the thread you are on enabling you to safely interact with it as long as you don't leave that thread.

    The best way to use Realm in a thread-safe way is to create a new reference to your Realm using RLMRealm *realm = [RLMRealm defaultRealm]; every time you move between threads and need to access your Realm. This way you can ensure that you will never get the incorrect thread exception.