首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >后台线程下载图片时的核心数据锁定

后台线程下载图片时的核心数据锁定
EN

Stack Overflow用户
提问于 2013-06-05 01:30:44
回答 2查看 538关注 0票数 1

我正在尝试异步下载图像并将它们存储在Core Data中。第一步是下载一个json文件,解析它,并在Core Data中为提要中的每个对象保存一个实体。这部分运行得很好。

假设我最终在核心数据中有10个Bird对象。每个Bird都有一个名称、描述等,并且与BirdImage之间存在多对多关系,这是它自己的实体。图像有一个"image_url“属性(字符串)和一个”BirdImage“属性(可转换)。

现在,当我到达将显示鸟类图片的应用程序的屏幕时,我首先检查BirdImage的"image“属性。如果它不是空的,我就将whateverBirdEntity.image设置为UIImageView的图像。如果为空,则需要下载镜像。在这样的代码中:

代码语言:javascript
复制
@property (nonatomic, strong) AssetRequest *assetRequest; //this is just a wrapper for an asset url, cache policy, and time out
@property (nonatomic, strong) NSURLRequest *assetURLRequest;
@property (nonatomic, strong) NSURLConnection *assetConnection;
@property (nonatomic, strong) NSMutableData *assetConnectionData;
@property (nonatomic, strong) BirdImage *imageEntity;
@property (nonatomic, strong) NSManagedObjectContext *objectContext;

...

- (void)load {

    dispatch_async(dispatchQueue, ^{

        //Check for the image in Core Data

        self.objectContext = [[NSManagedObjectContext alloc]
                              initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        self.objectContext.parentContext = [[CoreDataController sharedController] managedObjectContext];


        NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:@"BirdImage"];
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"image_url = %@", [self.assetRequest.assetURL absoluteString]];
        [fetch setPredicate:predicate];

        NSArray *objects = [self.objectContext executeFetchRequest:fetch error:nil];

        if ([objects count] > 0)
        {
            BirdImage *birdImage = [objects objectAtIndex:0];
            if (birdImage.image) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    BirdAsset *asset = [[BirdAsset alloc] init];

                    asset.url = [NSURL URLWithString:birdImage.image_url];
                    asset.image = birdImage.image;
                    if (self.successBlock)
                        self.successBlock(asset); //the caller will use asset.image for the UIImageView
                });

                return;
            }else{
                //no image found, need to download it
                self.imageEntity = birdImage; //this is the entity I want to re-save in Core Data once the image finishes downloading

                dispatch_async(dispatch_get_main_queue(), ^{

                    self.assetURLRequest = [NSURLRequest requestWithURL:self.assetRequest.assetURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:self.assetRequest.timeOut];

                    self.assetConnection = [[NSURLConnection alloc] initWithRequest:self.assetURLRequest delegate:self startImmediately:NO];
                    [self.assetConnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
                    [self.assetConnection start];

                });
            }

        }

        }];


    });    
}

然后,当下载完成时:

代码语言:javascript
复制
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

    dispatch_async(dispatchQueue, ^{

        UIImage *resultImage = [UIImage decompressImageFromData:self.assetConnectionData];
        NSData *resultData = UIImagePNGRepresentation(resultImage);

        DLog(@"saving to core data: %@", self.imageEntity.image_url); //THIS HAPPENS 10 TIMES (every time)

        self.imageEntity.image = resultImage;

            @try {
                NSError *saveError = nil;
                if (![self.objectContext save:&saveError])
                    NSLog(@"saveError %@", saveError);
            }
            @catch (NSException *exception) {
                NSLog(@"Exception: %@", exception);
            }


            [[CoreDataController sharedController] saveContext];


            BirdAsset *finalAsset = [[BirdAsset alloc] init];
            finalAsset.data = resultData;
            finalAsset.image = resultImage;
            finalAsset.url = [NSURL URLWithString:self.imageEntity.image_url];


            DLog(@"SUCCESS"); //THIS HAPPENS anywhere from 4-7 times. I never get all 10 images.

            dispatch_async(dispatch_get_main_queue(), ^{

                if (self.successBlock)
                    self.successBlock(finalAsset);
            });


    });
}

图像下载正常,当我检查我的数据库时,我可以看到每个BirdImage“图像”的BLOB数据。问题是,在10个图像中,实际上会显示随机数量的图像(第一次运行时会显示4-7个图像)。然后,如果我再次回到这个屏幕,应用程序将锁定,没有错误消息或崩溃。我怀疑这是某种核心数据锁定。

我知道我必须“从创建它的同一线程访问上下文”。但是,如果我在不同的方法中访问上下文(例如在上面的load和connectionDidFinishLoading方法中),我如何使用相同的线程?换句话说,我如何修改我的代码,以便在图像下载完成时执行线程安全的CoreData上下文保存?

EN

回答 2

Stack Overflow用户

发布于 2013-06-05 03:27:03

首先,我不确定您的图像是在单独的线程中下载的

代码语言:javascript
复制
 dispatch_async(dispatch_get_main_queue(), ^{
       self.assetURLRequest = [NSURLRequest requestWithURL:self.assetRequest.assetURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:self.assetRequest.timeOut];
       self.assetConnection = [[NSURLConnection alloc] initWithRequest:self.assetURLRequest delegate:self startImmediately:NO];
       [self.assetConnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
       [self.assetConnection start];

  });

dispatch_get_main_queue()返回与主线程相关联的主队列,因此您的[NSRunLoop currentRunLoop]将返回主线程运行循环,这不是很好。

其次,在数据库中将图像保存为blob并不是一个好主意,因为数据库大小将急剧增加,查询和其他操作将花费更长的时间来执行,因此您应该将它们保存在本地(文档目录)或缓存一段时间,并在数据库中仅保存图像的路径。

第三,self.imageEntity = birdImage这是不安全的,这行代码可能会被多次调用,而只有一个镜像被下载,所以你失去了对实体的引用,我认为这是你的镜像没有完全下载的主要原因。

第四,您应该使用AFNetworkingAFImageRequestOperation,这将负责异步下载,您可以通过比较图像URL和您的实体URL来保存图像。

票数 1
EN

Stack Overflow用户

发布于 2013-06-05 02:09:35

NSManagedObjectContext有一个名为performBlock: (或performBlockAndWait:)的方法,不出所料,它需要一个块。然后保证在上下文的线程上执行该块。您可以通过将connectionDidFinishLoading:中的代码放在传递给performBlock:的块中来利用它

代码语言:javascript
复制
void (^contextBlock)() = ... // your code here

[self.objectContext performBlock:contextBlock];

如果需要在不同的线程上进行UI更新或执行代码,也可以在该块中使用dispatch_async

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16923997

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档