在类别类方法实现内从块内部返回一个对象

问题描述:

我遇到了一个我的实现中遇到的问题,我并不知道如何解决。您能否提一些建议。在类别类方法实现内从块内部返回一个对象

我试图实现一个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 */ 
    } 
]; 
+0

谢谢你的回答。这是我正在寻找的解决方案。你可能也建议一些关于同步线程的源代码材料吗? – pit 2014-10-03 06:57:01

+0

对于iOS/OSX,最好考虑队列而不是线程的并发性。查看Apple的[并发编程指南](https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html)以获得有关此主题的良好概述;它应该有一些关于同步数据的信息。 – 2014-10-03 14:08:27

以外的块的调用[session downloadTaskWithRequest:.....]定义然后块内像这样

__block Photo *photoObject = nil; 

一个变量,你完成设置其属性前后,设置

photoObject = photo; 

现在你可以做任何你想要的块的外部是photoObject变量。 结账this Apple developer documentation on Blocks and Variables

+0

谢谢你的回答。这也是我最初的想法。我确实试图在发布我的原始问题之前定义__block Photo * photoObject。问题是,当我打电话(即在我的appDelegate)[Photo photoWithFlickrData:[self fetchPhoto] inManagedObjectContext:dbContext]它总是返回nil。我也看到块中的照片对象被创建(通过完成处理器的末尾的NSlog)。 – pit 2014-10-02 15:30:08

+2

在这些块内部发生了异步操作,所以在这种情况下仅仅创建一个'__block'变量不起作用。 – 2014-10-02 15:30:28