简体   繁体   English

循环的Swift 4完成处理程序不起作用

[英]Swift 4 completion handler for loop not working

In swift 4 I have this method that does a loop with a completion handler: 在Swift 4中,我有一个使用完成处理程序循环的方法:

func loopImages(qa: QAClass, assets: [DKAsset], completion: ([PhotoClass]) -> Void) {

        var imageCellHolder = [PhotoClass]()

            for x in assets
            {

                x.fetchOriginalImage(completeBlock: { (image, info) in

                     let compressedImage = image?.resizeWithWidth(width: 800)

                     let imageData:Data = compressedImage!.jpegData(compressionQuality: 0.5)!

                     imageCellHolder.append(PhotoClass(job: String(self.selectedCommunity! + self.selectedLot!), photo: imageData, itemId: qa.itemId))

                 })
            }

            completion(imageCellHolder)        
    }

And here is the method its suppose to call when the loop is done: 这是当循环完成时它应该调用的方法:

func loopFinished(imageCellHolder: [PhotoClass])
    {
        self.saveQAPhotos(cellHolder: imageCellHolder) { result in

            print(result)

        }
    }

And this is how I am calling the loop method: 这就是我调用循环方法的方式:

self.loopImages(qa: qa, assets: assets, completion: self.loopFinished)

My problem is the completion handler method (loopFinished) is called before the loop is finished. 我的问题是在循环结束之前调用了完成处理程序方法(loopFinished)。

In case you are wondering, fetchOriginalImage is from the library DKImagePickerController https://github.com/zhangao0086/DKImagePickerController 如果您想知道,fetchOriginalImage来自库DKImagePickerController https://github.com/zhangao0086/DKImagePickerController

You need a DispatchGroup for multiple asunchounous tasks finish completion 您需要一个DispatchGroup才能完成多个不幸的任务

func loopImages(qa: QAClass, assets: [DKAsset], completion: ([PhotoClass]) -> Void) {

    var imageCellHolder = [PhotoClass]()

    let dispa = DispatchGroup()

    for x in assets
    {
        dispa.enter()

        x.fetchOriginalImage(completeBlock: { (image, info) in

            let compressedImage = image?.resizeWithWidth(width: 800)

            let imageData:Data = compressedImage!.jpegData(compressionQuality: 0.5)!

            imageCellHolder.append(PhotoClass(job: String(self.selectedCommunity! + self.selectedLot!), photo: imageData, itemId: qa.itemId))

            dispa.leave()

        })
    }

    dispa.notify(queue: .main) {
         completion(imageCellHolder)
    }

}

The reason for this is that fetching the images takes time, the actual for loop completes very quickly and returns an empty array because the images haven't been fetched yet. 这样做的原因是,获取图像需要花费时间,实际的for循环非常快速地完成,并且由于尚未获取图像而返回空数组。

Consider using a DispatchGroup 考虑使用DispatchGroup

func loopImages(qa: QAClass, assets: [DKAsset], completion: ([PhotoClass]) -> Void) {

    var imageCellHolder = [PhotoClass]()
    let group = DispatchGroup()

    for x in assets
    {
        group.enter()
        x.fetchOriginalImage(completeBlock: { (image, info) in

            let compressedImage = image?.resizeWithWidth(width: 800)

            let imageData:Data = compressedImage!.jpegData(compressionQuality: 0.5)!

            imageCellHolder.append(PhotoClass(job: String(self.selectedCommunity! + self.selectedLot!), photo: imageData, itemId: qa.itemId))
            group.leave()

        })

        group.notify(queue: .main) {
            completion(imageCellHolder)
        }
    }
}

The DispatchGroup will wait for all images to finish downloading and will call the completion when they finish DispatchGroup将等待所有图像完成下载,并在完成时调用完成文件

Your tasks are asynchronous. 您的任务是异步的。 That means, you don't have data immediately after you request them. 这意味着,您在请求数据后不会立即拥有数据。 You need to wait then every image is fetched. 您需要等待,然后获取每个图像。

You can avoid using DispatchGroup and you can track if every image has been fetched by increasing some count variable. 您可以避免使用DispatchGroup并且可以通过增加一些计数变量来跟踪是否已获取每个图像。 After count of fetched images is the same as count of your assets array, you can call completion 在获取的图像数量与assets数组的数量相同之后,您可以调用completion

func loopImages(qa: QAClass, assets: [DKAsset], completion: ([PhotoClass]) -> Void) {

    var imageCellHolder = [PhotoClass]()

    var count = 0 // <--

    for x in assets {

        x.fetchOriginalImage { image, info in

            let compressedImage = image?.resizeWithWidth(width: 800)

            let imageData:Data = compressedImage!.jpegData(compressionQuality: 0.5)!

            imageCellHolder.append(PhotoClass(job: String(self.selectedCommunity! + self.selectedLot!), photo: imageData, itemId: qa.itemId))

  /* -> */  count += 1
            if count == assets.count {
                completion(imageCellHolder)
            }

        }
    }

}

这是因为fetchOriginalImage是异步的,您应该使用DispatchGroup之类的东西来跟踪所有异步调用何时完成

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM