领域从错误的线程访问

时间:2017-03-24 16:08:13

标签: ios objective-c multithreading realm

我编写了下面的方法来处理文件的下载。

- (void)downloadFileFromURL:(NSURL *)url {
    if (url != nil) {
        if ([url pathExtension] != nil) {
            RLMRealm *realm = [Utilities getRealm];

            RLMResults<Download *> *results = [[Download allObjectsInRealm:realm] sortedResultsUsingKeyPath:@"id" ascending:NO];
            Download *oldestDownload = [results firstObject];

            Download *download = [[Download alloc] init];
            download.id = oldestDownload.id+1;
            download.name = [url path];
            download.type = @"html";
            download.state = DownloadStateNew;
            download.token = [Utilities generateToken];
            download.url = [url absoluteString];

            [realm beginWriteTransaction];
            [realm addObject:download];
            self.current = download;
            [realm commitWriteTransaction];

            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:download.name];

            AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
            manager.responseSerializer = [AFHTTPResponseSerializer serializer];
            manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/plain", @"text/html", @"application/json", @"image/png", nil];
            [manager GET:[NSString stringWithFormat:@"%@", url]
              parameters:nil
                 success:^(AFHTTPRequestOperation *operation, id responseObject) {
                     NSData *data = [[NSData alloc] initWithData:responseObject];
                     [data writeToFile:path atomically:YES];
                     NSLog(@"successful download to %@", path);

                     RLMRealm *realm = [Utilities getRealm];
                     [realm beginWriteTransaction];
                     self.current.state = DownloadStateDone;
                     [realm commitWriteTransaction];
                 } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                     NSLog(@"Error: %@", error);


                     RLMRealm *realm = [Utilities getRealm];
                     [realm beginWriteTransaction];
                     self.current.state = DownloadStateCancelled;
                     [realm commitWriteTransaction];
                 }];
        }
    }
}

然而,这不断抛出从错误的线程访问Realm的错误。我不知道是什么造成了这种情况。有什么想法吗?

1 个答案:

答案 0 :(得分:1)

从AFNetworking代码判断,看起来successfailure块将在单独的调度队列上调用,而不是最初用于启动请求的调度队列。

为了保证ACID合规性,Realm对象受线程限制,如果您尝试将其传递给另一个线程然后访问其任何属性,则会触发您看到的异常。

我们最近在Realm Objective-C和Realm Swift中添加了一个功能,可以传递表示Realm对象的线程安全引用,然后可以使用这些引用查询新线程上的同一对象:

// Do work with `self.current`

RLMThreadSafeReference *currentReference = [RLMThreadSafeReference referenceWithThreadConfined:self.current];

AFHTTPRequestOperationManager *manager  =[AFHTTPRequestOperationManager manager];

//Configure `manager`

[manager GET:[NSString stringWithFormat:@"%@", url]
  parameters:nil
     success:^(AFHTTPRequestOperation *operation, id responseObject) {
         MyCurrentClass *threadCurrent = [RLMRealm resolveThreadSafeReference:currentReference];
         [threadCurrent.realm beginWriteTransaction];
         threadCurrent.state = DownloadStateDone;
         [threadCurrent.realm commitWriteTransaction];
     }
     failure:^(AFHTTPRequestOperation *operation, NSError *error) {
         // Handle `threadReference` here too if needed
     }];