Multithreading 保存使用AFNetworking下载的数据时是否需要使用多个ManagedObjectContext?

Multithreading 保存使用AFNetworking下载的数据时是否需要使用多个ManagedObjectContext?,multithreading,core-data,afnetworking,Multithreading,Core Data,Afnetworking,关于我所做工作的一些背景: 我从服务器上下载了一个CSV文件,其中包含许多小工具的数据。然后使用CSV解析器将数据解析为一组NSDictionary。完成后,下面的代码将字典中保存的信息写入核心数据 下面代码的快速说明: 获取所有现有的核心数据项 对于每个字典,检查产品代码是否与核心数据中已有的产品代码匹配 如果没有匹配项,则创建一个新的Gadget实体并保存其数据 如果存在匹配项,请使用最新信息进行更新 如果小工具是新的或现有手册已过期,请使用AFNetworking下载小工具的用户手册zip

关于我所做工作的一些背景:

我从服务器上下载了一个CSV文件,其中包含许多小工具的数据。然后使用CSV解析器将数据解析为一组NSDictionary。完成后,下面的代码将字典中保存的信息写入核心数据

下面代码的快速说明:

获取所有现有的核心数据项 对于每个字典,检查产品代码是否与核心数据中已有的产品代码匹配 如果没有匹配项,则创建一个新的Gadget实体并保存其数据 如果存在匹配项,请使用最新信息进行更新 如果小工具是新的或现有手册已过期,请使用AFNetworking下载小工具的用户手册zip文件。该手册是作为一个单独的实体创建的,它与小工具有一个关系,我已经这样做了,以防需要保存多个手册。 应用程序第一次执行此操作时,工作正常,即必须创建所有新的小工具,并为每个小工具下载手册。如果我在服务器上更新小工具的手册并再次运行该应用程序,我会得到一个错误,我在代码中标记了这一行:

非法尝试在不同上下文中的对象之间建立关系“theGadget”

实体都是在同一个MOC上创建的,所以我不确定为什么会出现错误。无论如何,我相信AFNetworking的回调是在主线程上的,所以我可以使用one MOC。也许我又犯了一个错误,或者有更好的方法来做我想做的事

-(void)populateCoreDataWithServerData {

    // Fetch all the existing info from core data
    theItems = [self loadFromDatabase:@"Gadgets" andSortDescriptor:nil];

    int row = 0;

    // Cycle through each entry in the parsed CSV data.
    for (NSDictionary *itemDictionary in theItemsInCSV) {

        // Check to see if we've downloaded this item previously.
        Gadgets *gadget;
        bool foundIt = FALSE;
        bool needToUpdateZip = TRUE;

        for (int i = 0; i < [theItems count]; ++i) {

            // Check if we have downloaded it already. If so, update as the item in core data with any new data.
            if ([[[theItems objectAtIndex:i] productCode] isEqualToString:[itemDictionary objectForKey:@"Product Code"]]) {

                foundIt = TRUE;
                gadget = [theItems objectAtIndex:i];

                NSLog(@"Found this item in the local database. Won't be downloading it again.");

                // Check if the zip file with the instruction manual needs updating.
                if ([[[theItems objectAtIndex:i] zipURL] isEqualToString:[itemDictionary objectForKey:@"Manual Zip"]]) {
                    NSLog(@"The zip file is the same or non-existant, no need to update.");
                    needToUpdateZip = FALSE;
                }
                else {
                    NSLog(@"The zip file is different. Will download");
                }
            }
        }

        // If we haven't downloaded it before, create a new item. We'll update the fields in that.
        if (foundIt == FALSE) {
            gadget = (Gadgets *)[NSEntityDescription insertNewObjectForEntityForName:@"Gadget" inManagedObjectContext:self.managedObjectContext];
            NSLog(@"Didn't find this item in the local database. Had better download it.");
        }

        // Update all the fields.
        [gadget setName:[itemDictionary objectForKey:@"gadget name"]];
        //Set some more attributes (product code etc)


        // Download gadget manual (zip file) if it exists
        if (needToUpdateZip == TRUE) {  // Check if there's a URL in there

            // Create a temp folder
            if (![[NSFileManager defaultManager] fileExistsAtPath:[self tempDirectory]) {  // Use Formidible Key as in identifier rather than the Product Code as the Form Key never has any spaces in it.

                NSError* error;
                if([[NSFileManager defaultManager] createDirectoryAtPath:[self tempDirectory] withIntermediateDirectories:YES attributes:nil error:&error]) {
                    NSLog(@"Directory created");
                }
            }

            NSString *fileName = [[NSString alloc] initWithFormat:@"Temp%i.zip", row];
            NSString *fileNameWithPath = [[self tempDirectory] stringByAppendingPathComponent:fileName];

            // Download the zip
            NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[itemDictionary objectForKey:@"Manual Zip"]]];
            AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

            operation.outputStream = [NSOutputStream outputStreamToFileAtPath:fileNameWithPath append:NO];

            [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
                NSLog(@"Successfully downloaded file to %@", [self tempDirectory]);

                // Save the individual files to core data.
                Manuals *manual = (ARData *)[NSEntityDescription insertNewObjectForEntityForName:@"Manuals" inManagedObjectContext:self.managedObjectContext];
                [manual setTheGadget:gadget];  // ERROR APPEARS HERE  **********************************
                // Extract the zip file and set some attributes
            }
             failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                 NSLog(@"Download didn't work. Error: %@", error);
                 }
             ];

            [operation start];
        }

        ++row;
    }

    NSError *error;
    if (![managedObjectContext save:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error localizedDescription]);
    }
}

提前谢谢

更新现有数据时,我的解决方法是获取与小工具关联的现有手动实体并覆盖其数据,而不是删除手动实体并重新创建


这不是一个真正的解决方案,所以如果你对上面的代码有什么错误的想法,请让我知道

我认为这是一个核心数据问题;您的AFN网络代码看起来不错。您是否在调用insertNewObjectForEntityForName:inManagedObjectContext:之前设置了断点,并确保self.managedObjectContext在这两种情况下都指向同一地址?谢谢您的评论。我已经检查过了,self.managedObjectContext的地址在每个位置都是相同的。我无法为您的问题提供精确的解决方案,这对我来说非常奇怪,但您可以使用此方法。您的代码看起来像NSManagedObjectID*gadgetId=gadget.objectID;[operation setCompletionBlockWithSuccess:^AFHTTPRequestOperation*operation,id responseObject{Manuals*manual=//创建手册[manual setTheGadget:[self.managedObjectContext objectWithId:gadgetId]]