2014-01-07 5 views
2

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

Проблема с приведенным ниже кодом заключается в том, что метод postToForm вызывается и выполняется до того, как мои convertedImages были добавлены. Как я могу это исправить, поэтому он ждет, чтобы это произошло? Или я все об этом ошибаюсь? (Мне нужно знать об использовании памяти тоже нужно освободить память после того, как сообщение было сделано.)

Вот мой код, который содержит эту проблему:

// create serial queue 
dispatch_queue_t queue = dispatch_queue_create("myQueue", 0); 

// create dispatch group 
dispatch_group_t group = dispatch_group_create(); 

for(id photos in photoUrls) { 
    NSString *urlString = photos; 

    // enqueue operation in queue 
    dispatch_async(queue, ^{ 
     // enter the group 
     dispatch_group_enter(group); 

     // do something async, I do use another dispatch_queue for example 
     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 
      // wrap in autoreleasepool to release memory upon completion 
      @autoreleasepool { 
       // here for example the nested operation sleeps for two seconds 
       NSURL *url = [NSURL URLWithString:urlString]; 
       ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; 

       [library assetForURL:url resultBlock:^(ALAsset *asset) { 

        //TODO: Deal with JPG or PNG 
        NSData *imageData = UIImageJPEGRepresentation([self thumbnailForAsset:asset maxPixelSize:1000], 0); 
        NSString *base64 = [imageData base64EncodedString]; 
        NSLog(@"base64::%@",base64); 
        [convertedImages addObject:base64]; 

       } failureBlock:^(NSError *error) { 
        NSLog(@"that didn't work %@", error); 
       }]; 


       dispatch_group_leave(group); 
      } 
     }); 

     // wait until the nested async operation leaves the group (e.g finishes its job) 
     dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 

     NSLog(@"Finished single operation."); 
    }); 
} 

// will be called once all operations complete 
dispatch_async(queue, ^{ 
    NSLog(@"Finished all jobs. "); 
    NSLog(@"how many::%lu",(unsigned long)convertedImages.count); 
    [jsonWithPhotos setObject:convertedImages forKey:@"photo64"]; 
    [self postToForm]; 
}); 

ответ

4

Причина, по которой вы вызываете свой блок завершения до того, как все изображения загружены, заключается в том, что вы вызываете dispatch_group_leave(group); в неположенном месте. Вы должны вызвать его в блоке результатов (и блоке ошибок) ALAssetsLibrary. Остальное верно (на самом деле точно так же, как Apple предлагает реализовать блок завершения очереди: поскольку приватная очередь GCD является сериальной, блок завершения - это не что иное, как последний блок самой очереди).

EDIT: Как уже было сказано: Дело в том, что ALAssetsLibrary занимает некоторое время, чтобы извлечь данные по запрошенному URL-адресу (на самом деле есть блок завершения, который указывает, что операция асинхронна). Итак, сразу после запроса изображения для текущего URL-адреса вы освободили семафор, созданный dispatch_group_enter, и, вероятно, это произошло до того, как ALAssetsLibrary сможет выполнить блок завершения.

Теперь, заметьте, что, как вы реализуете блок завершения Очереди действителен для последовательной очереди только с прошивки 5 + вы можете создать параллельную очередь (как глобальная очередь), указав DISPATCH_QUEUE_CONCURRENT как тип очереди и там это вид подхода больше недействителен.Если вместо того, чтобы иметь последовательную очередь, у вас был одновременный, в этом случае я бы проверил, был ли текущий url последним в блоке результатов ALAssetsLibrary и назвал там блок завершения.

Для полноты вы можете использовать dispatch_semaphore_t для обработки таких ситуаций, которые логически правильнее, чем использование группового ожидания (здесь ваша группа состоит из 1 операции), но, повторяю, только логически лучше, результаты та же.

Все, что я написал, что написано в Apple's concurrency programming guide

+0

О, человек, на который я потратил столько времени на такой простой ответ! Позаботьтесь, чтобы объяснить, почему это работает правильно? – BluGeni

-1

Если у вас есть зависимость, вы» Я хочу использовать NSOperationQueues. С очередями операций вы можете установить зависимость между операциями, чтобы один (или более) не выполнялся до тех пор, пока не завершится конкретная операция.

Так, например, используя блоки (например, те, что у вас есть), вы можете реализовать это как-то следующим образом.

NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock:^{ /* your work */ }]; 
NSBlockOperation* dependentOperation = [NSBlockOperation blockOperationWithBlock:^{ /* dependent work */ }]; 

[dependentOperation addDependency:operation]; 

NSOperationQueue* q = [[NSOperationQueue alloc] init]; 
[q addOperations:@[ operation, dependentOperation] waitUntilFinished:NO]; 

NSOperationQueues также купить вам несколько аккуратных вещей, как быть в состоянии справиться с отмен, которые могут пригодиться, если вы беспокоитесь об отмене postToForm, если есть сбои в другом месте.

И если вы беспокоитесь о памяти, вы можете установить максимальное количество одновременных операций как часть вашего NSOperationQueue.

q.maxConcurrentOperationCount = 5; 
+0

Означает ли это, мне нужно иметь другой способ делать блоки или я могу реализовать NSOperationQueues с тем, что у меня есть? – BluGeni

+0

Если я правильно понимаю ваш код, я считаю, что вы можете создать все операции манипулирования изображениями, а затем заключительные операции с сообщениями. Вы можете установить зависимость для операции post, которая будет зависеть от всех операций манипуляции с изображениями. Операции манипулирования изображением включали бы все в autoreleasepool. Отвечает ли это на ваш вопрос? – Travis

0

Если вы хотите использовать dispatch_async для всего этого, вам придется немного по-другому функционировать. Форма, которую я могу вам сказать, просто пытается сделать все это в фоновом режиме. Есть несколько способов сделать это, но я вижу, что ваш код идет в одном блоке в фоновом потоке, а затем перезвонит, когда он будет завершен. Это также будет наиболее эффективным с точки зрения памяти (не вздутие живота), потому что оно запускает одно преобразование одновременно, а не пытается сделать все сразу и заполнить вашу доступную память (и, возможно, сбой).

dispatch_async into queue 
{ 

    for loop to do all of your images 
    { 

    } 

    dispatch_async into main thread saying finished queue 
    { 
     //function call or block with executing code 
    } 
} 

Wrap все, что в autoreleasepool только для дополнительного управления памятью удовольствия.

0

Зачем запуская каждую задачу в цикле в качестве отдельной асинхронной работы? Запустите весь цикл как одну задачу, и в этом обработчике завершения вызовите функцию из вашего основного потока в postToForm.

Что-то вроде

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 
             (unsigned long)NULL), ^(void) { 
     [self convertImages]; 

    }); 

    // Call this on background thread 
    - (void)convertImages { 

     for(id photos in photoUrls) { 
     // Do stuff 
     } 

     // Now call function on main thread 
     [[NSOperationQueue mainQueue] addOperationWithBlock:^ { 
       [self updateUI]; 
     }]; 
    } 

    // Must call this on main thread because we update the UI 
    - (void)updateUI { 
      NSLog(@"Finished all jobs. "); 
      NSLog(@"how many::%lu",(unsigned long)convertedImages.count); 
      [jsonWithPhotos setObject:convertedImages forKey:@"photo64"]; 
      [self postToForm]; 
    } 
Смежные вопросы