在类别类方法实现内从块内部返回一个对象
我遇到了一个我的实现中遇到的问题,我并不知道如何解决。您能否提一些建议。在类别类方法实现内从块内部返回一个对象
我试图实现一个NSManagedObject类类照片+ Flickr.m一个类的方法+(无效)photoWithFlickrData:inManagedObjectContext:
我想什么做的是使用NSURLSessionDownloadTask来自Flickr API下载数据和然后创建Photo对象并将这个新创建的对象插入数据库(如果它尚未存在)。这部分工作正常。
最后,我想返回新创建的(或在db中找到的对象)Photo对象。这就是我遇到问题的地方。由于我使用的是类别,我不能使用实例变量。我无法找到任何好的解决方案从这个completionHandler块中获取这个Photo对象。
我的代码:
@implementation Photo (Flickr)
+ (void)photoWithFlickrData:(NSDictionary *)photoDictionary
inManagedObjectContext:(NSManagedObjectContext *)context
{
NSString *placeId = [photoDictionary valueForKeyPath:FLICKR_PHOTO_PLACE_ID];
NSURL *urlInfoAboutPlace = [FlickrFetcher URLforInformationAboutPlace:placeId];
NSURLRequest *request = [NSURLRequest requestWithURL:urlInfoAboutPlace];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDownloadTask *task =
[session downloadTaskWithRequest:request
completionHandler:^(NSURL *localfile, NSURLResponse *response, NSError *error) {
if(!error) {
NSData *json = [NSData dataWithContentsOfURL:localfile];
NSDictionary *flickrPlaceDictionary = [NSJSONSerialization JSONObjectWithData:json
options:0
error:NULL];
dispatch_async(dispatch_get_main_queue(), ^{
Photo *photo = nil;
// flickr photo unique id
NSString *uniqueId = [photoDictionary valueForKeyPath:FLICKR_PHOTO_ID];
NSFetchRequest *dbRequest = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
dbRequest.predicate = [NSPredicate predicateWithFormat:@"uniqueId = %@", uniqueId];
NSError *error;
NSArray *reqResults = [context executeFetchRequest:dbRequest error:&error];
if (!reqResults || error || [reqResults count] > 1) {
//handle error
} else if ([reqResults count]) {
//object found in db
NSLog(@"object found!");
photo = [reqResults firstObject];
} else {
//no object in db so create a new one
NSLog(@"object not found, creating new one");
photo = [NSEntityDescription insertNewObjectForEntityForName:@"Photo"
inManagedObjectContext:context];
//set its properties
photo.uniqueId = uniqueId;
photo.title = [photoDictionary valueForKey:FLICKR_PHOTO_TITLE];
photo.region = [FlickrFetcher extractRegionNameFromPlaceInformation:flickrPlaceDictionary];
NSLog(@"title: %@", photo.title);
NSLog(@"ID: %@", photo.uniqueId);
NSLog(@"region: %@", photo.region);
}
});
}
}];
[task resume];
//how to get Photo *photo object???
//return photo;
}
我真的很感激就如何落实这一任何建议。
由于您的块内发生了异步操作,因此您需要将完成处理程序(块)传递给photoWithFlickrData:inManagedObjectContext:
方法,并在拥有有效照片数据时调用它。
您需要为您的方法添加一个新参数,以便您可以传递完成处理程序。我会做这样的事情:
+ (void)photoWithFlickrData:(NSDictionary *)photoDictionary
inManagedObjectContext:(NSManagedObjectContext *)context
withCompletionHandler:(void(^)(Photo *photo))completionHandler
然后,当你有一个有效的photo
对象,调用completionHandler
像这样:
completionHandler(photo);
它看起来就像你希望把他们在很你传递给dispatch_async
块的结尾:
/* ... */
dispatch_async(dispatch_get_main_queue(), ^{
Photo *photo = nil;
/* ... */
completionHandler(photo);
});
/* ... */
然后,你可以打电话给你的方法,像这样:
[Photo photoWithFlickrData:photoDictionary
inManagedObjectContext:context
withCompletionHandler:^(Photo* photo) {
/* use your valid photo object here */
}
];
以外的块的调用[session downloadTaskWithRequest:.....]
定义然后块内像这样
__block Photo *photoObject = nil;
一个变量,你完成设置其属性前后,设置
photoObject = photo;
现在你可以做任何你想要的块的外部是photoObject
变量。 结账this Apple developer documentation on Blocks and Variables。
谢谢你的回答。这也是我最初的想法。我确实试图在发布我的原始问题之前定义__block Photo * photoObject。问题是,当我打电话(即在我的appDelegate)[Photo photoWithFlickrData:[self fetchPhoto] inManagedObjectContext:dbContext]它总是返回nil。我也看到块中的照片对象被创建(通过完成处理器的末尾的NSlog)。 – pit 2014-10-02 15:30:08
在这些块内部发生了异步操作,所以在这种情况下仅仅创建一个'__block'变量不起作用。 – 2014-10-02 15:30:28
谢谢你的回答。这是我正在寻找的解决方案。你可能也建议一些关于同步线程的源代码材料吗? – pit 2014-10-03 06:57:01
对于iOS/OSX,最好考虑队列而不是线程的并发性。查看Apple的[并发编程指南](https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html)以获得有关此主题的良好概述;它应该有一些关于同步数据的信息。 – 2014-10-03 14:08:27