并发,引用并发执行代码中捕获的var

问题描述 投票:0回答:1

我想使用

enumerateContacts(with:usingBlock:)
async/await
方法获取用户的联系人。这是我的功能:

func fetchContacts() async throws -> [Contact] {
        let keys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactEmailAddressesKey]
        let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
        let store = CNContactStore()
        let contactsActor = ContactsActor()
        var contactsArray: [Contact] = []

        return try await withCheckedThrowingContinuation { continuation in
           DispatchQueue.global(qos: .background).async {
                do{
                    try  store.enumerateContacts(with: request) { contact, stop in
                        let contact = Contact(
                            givenName: contact.givenName,
                            familyName: contact.familyName,
                            emails: contact.emailAddresses.map { $0.value as String }
                        )
                        Task {
                             await contactsActor.appendToContacts(contact: contact)
                        }
                    }
                    continuation.resume(returning: contactsArray )
                } catch {
                    continuation.resume(throwing: error)
                }
            }
        }
    }

我也在使用这个演员:

actor ContactsActor {
    var contacts:[Contact] = []

    func appendToContacts(contact: Contact) {
        contacts.append(contact)
    }
    
    func getContact()-> [Contact]{
       return contacts
    }
}

在 viewDidLoad 方法中,我在任务中调用

fetchContacts
函数:

 Task {
            let contacts = try await fetchContacts()
            
            await MainActor.run(body: {
                self.contactsTable.reloadData()
            })
        }

我收到此错误:

在并发执行的代码中引用捕获的 var 'contactsArray'。

continuation.resume(returning: contactsArray )
前面 我正在学习快速并发,但我不知道如何解决这个错误。任何帮助将不胜感激。

我希望在名为

Contact
的自定义结构数组中获取用户的联系人。

swift concurrency
1个回答
0
投票

我会避免使用 GCD API,而是遵循 Swift 并发模式(例如分离任务):

func fetchContacts() async throws -> [Contact] {
    let task = Task.detached {
        let keys = [
            CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
            CNContactEmailAddressesKey as CNKeyDescriptor
        ]
        let request = CNContactFetchRequest(keysToFetch: keys)
        let store = CNContactStore()
        let formatter = CNContactFormatter()
        formatter.style = .fullName

        var contacts: [Contact] = []

        try store.enumerateContacts(with: request) { contact, stop in
            guard !Task.isCancelled else {
                stop.pointee = true
                return
            }

            let contact = Contact(
                givenName: contact.givenName,
                familyName: contact.familyName,
                fullName: formatter.string(from: contact),
                emails: contact.emailAddresses.map { $0.value as String }
            )
            contacts.append(contact)
        }

        try Task.checkCancellation()
        return contacts
    }

    return try await withTaskCancellationHandler {
        try await task.value
    } onCancel: {
        task.cancel()
    }
}

以上我也支持取消

  • 退出枚举 if
    Task.isCancelled
    ;和
  • CancellationError
    投掷
    Task.checkCancellation()

另外,如果你原谅我,我为你的

Contact
类型引入了一个额外的全名参数,并使用
CNContactFormatter
来构建这个字符串。这是个人喜好的问题,所以在那里做你喜欢做的事。这与更广泛的问题无关。

© www.soinside.com 2019 - 2024. All rights reserved.