Возврат объекта из внутреннего блока в рамках реализации метода класса категории

У меня возникла определенная проблема с моей реализацией, которую я действительно не знаю, как решить. Могли бы вы, пожалуйста, посоветовать.

Я пытаюсь реализовать класс категории NSManagedObject Photo+Flickr.m с одним методом класса +(void)photoWithFlickrData:inManagedObjectContext:

Я хотел бы загрузить данные из API Flickr с помощью NSURLSessionDownloadTask, а затем создать объект Photo и вставить этот новый созданный объект в базу данных (если его еще нет). Эта часть работает нормально.

И в конце я хотел бы вернуть новый созданный (или объект, который был найден в БД) объект Photo. И тут я сталкиваюсь с проблемой. Поскольку я использую категорию, я не могу использовать переменные экземпляра. Я не могу найти никакого хорошего решения, чтобы получить этот объект Photo из этого блока завершенияHandler.

Мой код:

@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;
}

Я был бы очень признателен за любые предложения о том, как это реализовать.


person pit    schedule 02.10.2014    source источник


Ответы (2)


Поскольку внутри ваших блоков происходят асинхронные операции, вам нужно будет передать обработчик завершения (блок) в ваш метод 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 */
     }
];
person Mike S    schedule 02.10.2014
comment
Спасибо за ваш ответ. Это решение, которое я искал. Не могли бы вы также посоветовать некоторые исходные материалы о синхронизации потоков? - person pit; 03.10.2014

Вне вашего блока перед вызовом [session downloadTaskWithRequest:.....] определите переменную, подобную этой

__block Photo *photoObject = nil;

Затем внутри блока после того, как вы закончите настройку его свойств, установите

photoObject = photo;

Теперь вы можете делать с переменной photoObject все, что хотите, вне блока. Ознакомьтесь с эта документация для разработчиков Apple по блокам и переменным.

person chriszumberge    schedule 02.10.2014
comment
Спасибо за ваш ответ. Это была и моя первоначальная идея. Я пытался определить __block Photo* photoObject, прежде чем публиковать здесь свой оригинальный вопрос. Проблема в том, что когда я вызываю (т.е. в моем appDelegate) [Photo photoWithFlickrData:[self fetchPhoto] inManagedObjectContext:dbContext], он всегда возвращает ноль. Я также вижу, что фотообъект в блоке создается (через NSlog в конце завершенияHandler). - person pit; 02.10.2014
comment
Внутри этих блоков происходят асинхронные операции, поэтому простое создание переменной __block в этом случае не сработает. - person Mike S; 02.10.2014