- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试异步下载图像并将它们存储在 Core Data 中。第一步是下载一个 json 文件,解析它,并在 Core Data 中为提要中的每个对象保存一个实体。那部分工作正常。
假设我在 Core Data 中有 10 个 Bird 对象。每个 Bird 都有名称、描述等,并且与 BirdImage 之间存在一对多关系,BirdImage 是它自己的实体。 BirdImage 有一个“image_url”属性(字符串)和一个“image”属性(可转换)。
现在,当我到达应用程序中将显示鸟类图片的屏幕时,我首先检查 BirdImage 的“图像”属性。如果它不为空,我只是将 whateverBirdEntity.image 设置为 UIImageView 的图像。如果为空,我需要下载图像。在这样的代码中:
@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];
});
}
}
}];
});
}
然后,当下载完成时:
- (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 上下文保存?
最佳答案
首先我不确定你的图片是在单独的线程上下载的
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
这不安全,这行代码可能会被调用多次而只下载一张图像,因此您丢失了对实体的引用,我认为这是您的图像未完全下载的主要原因。
第四,你应该依靠使用 AFNetworking
和 AFImageRequestOperation
这将处理异步下载,你可以通过比较图像 URL 和你的实体 URL 来保存图像。
关于ios - 在后台线程上下载图像时锁定核心数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16923997/
我是一名优秀的程序员,十分优秀!