在Swift中执行异步函数完成后,在尾随闭包之外获取完全填充的数组

时间:2018-08-19 19:20:06

标签: swift asynchronous

我正在尝试使用来自extractProperties()函数的异步调用的结果填充finalArray。

class ViewController: UIViewController {

var finalArray: [SingleRepository] = []

let extractor = Extractor()

override func viewDidLoad() {
    super.viewDidLoad()

    print("Starting the program... ")

    extractor.extractProperties { object, error in
        guard let object = object else {
            print("Extractor did not reutrn data")
            return
        }
        self.finalArray.append(object)
        print("Appended successfully --- \(self.finalArray.count) --- inside the trailing closure")

    }

    print("Size of the array --- \(self.finalArray) --- outside the trailing closure")


}

问题是我无法使完全填充的finalArray在尾随闭包的范围之外使用! 输出日志:

    Starting the program... 
Size of the array --- [] --- outside the trailing closure
Appended successfully --- 1 --- inside the trailing closure
Appended successfully --- 2 --- inside the trailing closure
Appended successfully --- 3 --- inside the trailing closure
.
.
.
Appended successfully --- 300 --- inside the trailing closure

我知道为什么首先要从外部执行print语句,但是我永远无法获得其中包含所有300个对象的完全填充的数组。

请注意,以下帖子无法解决我的问题:Run code only after asynchronous function finishes executing

我什至通过编写以下函数来尝试解决该问题:

func constructingFinalArray(completionBlock: @escaping ([SingleRepository]) -> Void) {
        var fArrray: [SingleRepository] = []
        extractor.extractProperties { data, error in
            guard let data = data else {
                print("Extractor did not reutrn data")
                return
            }
            fArrray.append(data)
            completionBlock(fArrray)
        }
    }

并在viewDidLoad()中按如下方式调用它,但令人困惑的是我得到了相同的结果,并且数组中的每个元素都被填充,因此永远无法从尾随闭包中访问完全填充的数组!

constructingFinalArray { array in
        print("array size from constructingFinalArray function: \(array.count) ")
    }

输出:

    Starting the program... 
array size from constructingFinalArray function: 1 
array size from constructingFinalArray function: 2 
array size from constructingFinalArray function: 3
.
.
.

extractProperties被准确调用300次,有时它不返回日期(错误)。

    // Class to extract the required properties and
// provide ready to use object for ViewController
class Extractor {
    private let client = RepoViewerAPIClient()
    private var allURLs: [RepositoryURL] = []
    var allRepositories: [SingleRepository] = []


    // Method to extract all the required properties
    // compromising of 2 asynchrounous call, (nested asynch call)
    // one to get the urls and another call within the first call to extract all the propreties
    func extractProperties(completionHandler: @escaping (SingleRepository?, RepoViewerErrors?) -> Void) {
        // implementation of nested asynchronous calls are deleted to shorten the question length 


    }

}

1 个答案:

答案 0 :(得分:4)

似乎您打过一次电话

extractor.extractProperties {
    ...
}

闭包被称为恰好300 次,但有时无法返回任何数据。

在这种情况下,您可以采用这种方法。

extractor.extractProperties { object, error in
    serialQueue.async { [weak self] in

        count += 1

        guard count < 300 else  {
            self?.didCompletePopulation()
            return
        }

        guard let object = object else {
            print("Extractor did not reutrn data")
            return
        }

        self?.finalArray.append(object)

    }
}

func didCompletePopulation() {
    // be aware, this is not called on the main thread
    // if you need to update the UI from here then use the main thread
    print("Final array is populated \(self.finalArray)")
}

它如何工作?

该闭包的主体被包装到另一个通过串行队列执行的闭包中。这样,我们可以确保安全访问共享资源(finalArray和计数)。

serialQueue.async { [weak self] in
    ...
}

接下来,每次执行关闭操作都会使count增加1

然后我们确保计数小于300,否则我们将停止执行调用didCompletePopulation()的闭包。

guard count < 300 else  {
    self?.didCompletePopulation()
    return
}

我们检查结果是否包含正确的值,否则我们将停止执行当前闭包

guard let object = object else {
    print("Extractor did not reutrn data")
    return
}

最后我们将新元素添加到数组

self?.finalArray.append(object)