Код внешнего цикла выполняется во время цикла

#ios #swift #loops

#iOS #swift #циклы

Вопрос:

Я пытаюсь получить набор изображений, выбранных пользователем в PHPicker, и сохранить их в массиве. Я повторяю массив результатов PHPicker и добавляю на каждой итерации загруженное изображение в переменную. По какой-то причине код, который следует за этой итерацией, вызывается до завершения итерации. Я пробовал циклы while и for, удалил DispatchQueue.main.async, кажется, ничего не меняет порядок вызовов. Вероятно, очень очевидно, что я делаю неправильно, но я действительно этого не понимаю.

 var itemProviders: [NSItemProvider] = []
var iterator: IndexingIterator<[NSItemProvider]>?
var images = [UIImage]()


func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
    picker.dismiss(animated: true)
    
    itemProviders = results.map(.itemProvider)
    iterator = itemProviders.makeIterator()
    
    while let itemProvider = iterator?.next(), itemProvider.canLoadObject(ofClass: UIImage.self) {
        print("n Entered while")
        itemProvider.loadObject(ofClass: UIImage.self) { image, error in
            DispatchQueue.main.async {
                guard let image = image as? UIImage else { return }
                self.images.append(image)
                print("Image: (image)")
                print("Images: (self.images)")
            }
        }
    }
    displayAndStoreImages()
}


func displayAndStoreImages(){
    print("n Entered displayAndStoreImages()")
    print("Images: (images) n")
    
    (...)
}
  

Это результат выбора 2 изображений в PHPicker. выполняются 2 итерации, но не код внутри блока итерации. Вместо этого выполняется следующий код, и только после этого вызывается код внутри блока итерации. Почему код вне цикла выполняется во время цикла? TIA

  Entered while
 Entered while

 Entered displayAndStoreImages()
 Images: [] 

 Image: <UIImage:0x6000010ec3f0 anonymous {4288, 2848}>
 Images: [<UIImage:0x6000010ec3f0 anonymous {4288, 2848}>]
 Image: <UIImage:0x6000010d4630 anonymous {3000, 2002}>
 Images: [<UIImage:0x6000010ec3f0 anonymous {4288, 2848}>, <UIImage:0x6000010d4630 anonymous {3000, 2002}>]
  

Комментарии:

1. Поскольку вы выполняете асинхронные вызовы при загрузке изображений, функция displayAndStoreImages() вызывается до загрузки файлов и выполнения закрытия

2. @JoakimDanielson Я получаю те же результаты без DispatchQueue.main.async, я пробовал это перед публикацией.

3. itemProvider.loadObject является асинхронной функцией… так вот почему вы получаете его с / без DispatchQueue.main.async .

4. @NewDev Спасибо за дополнительные разъяснения, теперь я понял!

5. @NewDev Каковы альтернативы синхронному вызову? Я пробовал несколько альтернативных решений, но, похоже, ничего не работает. Я просто хочу получить выбранные изображения в исходном размере.

Ответ №1:

Для такого рода асинхронной работы вам нужно что-то вроде DispatchGroup .

Отредактировал ваш пример, чтобы использовать его

     func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
    picker.dismiss(animated: true)
    
    itemProviders = results.map(.itemProvider)
    iterator = itemProviders.makeIterator()
    
    let dispatchGroup = DispatchGroup()
    
    while let itemProvider = iterator?.next(), itemProvider.canLoadObject(ofClass: UIImage.self) {

        dispatchGroup.enter()

        print("n Entered while")
        itemProvider.loadObject(ofClass: UIImage.self) { image, error in
            DispatchQueue.main.async {
                guard let image = image as? UIImage else { return }
                self.images.append(image)
                print("Image: (image)")
                print("Images: (self.images)")

                dispatchGroup.leave()
            }
        }
    }
    dispatchGroup.notify(queue: DispatchQueue.main) {
        self.displayAndStoreImages()
    }
}


func displayAndStoreImages(){
    print("n Entered displayAndStoreImages()")
    print("Images: (images) n")
    
}
  

Комментарии:

1. Извините за поздний ответ! Спасибо за ваш ответ, полезно знать о DispatchGroup. Решение работает, но по какой-то причине между закрытием средства выбора и отображением изображений проходит несколько секунд.

2. Задержка не связана с предлагаемым решением, моя ошибка.