gpt4 book ai didi

Swift:无法链接 DispatchGroup

转载 作者:行者123 更新时间:2023-11-30 11:11:36 25 4
gpt4 key购买 nike

我正在尝试链接两个调度组,但我的代码总是在随机位置崩溃。

我的功能是这样的:

func getAllActivities(userUID: String, _ completionHandler: @escaping (_ activities: [ActivitiesForUIStruct]?, _ error: Error?) -> Void) {
let downloadGroup = DispatchGroup()
let detailGroup = DispatchGroup()

let _ = DispatchQueue.global(qos: .userInitiated)

var activities = [ActivitiesForUIStruct]()

getActivities(userUID: userUID) { (results, error) in
//Handle error

downloadGroup.enter()
if let results = results {
var groupTitle = ""
var originatorName = ""
var sourceName = ""

results.forEach({ (activity) in
let originatorUID = activity.originatorUserUID ?? ""
let groupID = activity.inspectionGroupID ?? ""
let sourceID = activity.sourceID ?? ""

detailGroup.enter()
self.getTitle(key: "inspectionGroups", value: groupID, { (title, error) in
...

if let title = title {
groupTitle = title
detailGroup.leave()
}
})

detailGroup.enter()
self.getName(userUID: originatorUID, { (name, error) in
...

if let name = name {
originatorName = name
detailGroup.leave()
}
})

detailGroup.enter()
self.getTitle(key: "users", value: sourceID, { (title, error) in
...

if let title = title {
sourceName = title
detailGroup.leave()
}
})

detailGroup.notify(queue: .global(qos: .userInitiated), execute: {
let activityUI = ActivitiesForUIStruct(activities: activity, originatorName: originatorName, sourceName: sourceName, inspectionGroupTitle: groupTitle)
activities.append(activityUI) //crash here
downloadGroup.leave() //crash here
})
})
}

downloadGroup.notify(queue: .main, execute: {
completionHandler(activities, nil)
})
}
}

我的代码的目的是从 UID 中获取名称和标题,然后将其附加到自定义的 struct 中。但是,我的实现往往会在 detailGroup.notify 中标记的区域崩溃。有人会建议我哪里出了问题吗?

注意:我的函数调用是正确的,即我能够打印出 originatorNamesourceNamegroupTitle

最佳答案

首先,

你需要平衡enter()leave() 。您的代码调用 downloadGroup.enter()仅一次出results.forEach ,但是downloadGroup.leave()在循环内被调用多次。所以,如果results有两个或多个元素,downloadGroup.leave()被调用的次数多于 downloadGroup.enter() .

<小时/>

第二,

一些看似异步的方法可能会同步调用完成处理程序。 (例如,当缓存命中时。)因此,一些 detailGroup.leave()可能会在 detailGroup.enter() 之前调用.

detailGroup.enter() +1
detailGroup.leave() 0 --> detailGroup.notify fires!
detailGroup.enter() +1
detailGroup.leave() 0 --> detailGroup.notify fires!
detailGroup.enter() +1
detailGroup.leave() 0 --> detailGroup.notify fires!

( detailGroup.notify 每次内部计数获取 0 时触发。)

如上所示,这可能会导致detailGroup.notify多次开火。使detailGroup.notify所有任务完成后只触发一次,需要保证所有 detailGroup.enter()在任何 detailGroup.leave() 之前执行.

detailGroup.enter() +1
detailGroup.enter() +2
detailGroup.enter() +3
detailGroup.leave() +2
detailGroup.leave() +1
detailGroup.leave() 0 --> detailGroup.notify fires!
<小时/>

第三,

swift Array不是线程安全的。所以,如果你的activities可以在多线程中更新,这可能会导致意外行为,包括崩溃。最好在 UI 线程中更新这样的数组。

<小时/>

考虑到所有这些因素,您的代码将如下所示:

func getAllActivities(userUID: String, _ completionHandler: @escaping (_ activities: [ActivitiesForUIStruct]?, _ error: Error?) -> Void) {
let downloadGroup = DispatchGroup()
let detailGroup = DispatchGroup()

var activities = [ActivitiesForUIStruct]()

getActivities(userUID: userUID) { (results, error) in
//Handle error

if let results = results {
var groupTitle = ""
var originatorName = ""
var sourceName = ""

results.forEach({ (activity) in
//### 1. Balance enter() and leave()
downloadGroup.enter()

let originatorUID = activity.originatorUserUID ?? ""
let groupID = activity.inspectionGroupID ?? ""
let sourceID = activity.sourceID ?? ""

//### 2. Guarantee all enter() executed before leave()
detailGroup.enter() //for getTitle(key: "inspectionGroups"...
detailGroup.enter() //for getName(userUID: originatorUID...
detailGroup.enter() //for getTitle(key: "users"...

self.getTitle(key: "inspectionGroups", value: groupID, { (title, error) in
//...
if let title = title {
groupTitle = title
}
detailGroup.leave()
})

self.getName(userUID: originatorUID, { (name, error) in
//...
if let name = name {
originatorName = name
}
detailGroup.leave()
})

self.getTitle(key: "users", value: sourceID, { (title, error) in
//...
if let title = title {
sourceName = title
}
detailGroup.leave()
})

//### 3. Use main queue to safely modify thread-unsafe objects
detailGroup.notify(queue: .main, execute: {
let activityUI = ActivitiesForUIStruct(activities: activity, originatorName: originatorName, sourceName: sourceName, inspectionGroupTitle: groupTitle)
activities.append(activityUI)
downloadGroup.leave()
})
})
}

downloadGroup.notify(queue: .main, execute: {
completionHandler(activities, nil)
})
}
}

在某些情况下,可能不需要我的一些建议,但在使用 DispatchGroup 或异步任务时,您最好记住所有 3 件事(Code Different 建议的 +1 件事)。

关于Swift:无法链接 DispatchGroup,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52137863/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com