Параллелизм, ссылка на захваченную переменную в одновременно выполняющемся коде

Я хочу получить контакты пользователя, используя методы 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()
    })
}

Перед continuation.resume(returning: contactsArray ) я получаю эту ошибку:

Ссылка на захваченную переменную 'contactsArray' в одновременно исполняемом коде.

Я изучаю параллелизм Swift и точно не знаю, как решить эту ошибку.

Я ожидал получить контакты пользователя в массиве специальной структуры с именем Contact.

Кстати, Task {…} в viewDidLoad уже есть на главном актере (поскольку viewDidLoad изолирован от главного актера в результате изоляции UIViewController от главного актера), поэтому MainActor.run {…} не нужен.

Rob 06.08.2024 02:34
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
56
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я бы избегал API GCD и вместо этого следовал шаблонам параллелизма 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 для построения этой строки. Это вопрос личных предпочтений, поэтому делайте в этом отношении все, что хотите. Это не имеет отношения к более широкому вопросу.

Все хорошо, но здесь есть небольшая административная проблема; этот ОП, вместо того, чтобы ждать повторного открытия stackoverflow.com/review/reopen/36309704 (что почти и есть), поторопился и повторил вопрос. Это неправильное поведение.

matt 06.08.2024 02:16

Я не могу его полностью винить. Столько вопросов, закрытых по тем или иным причинам, никогда не открываются вновь. И прошла почти неделя, так сколько ему ждать? Возможно, ему следует просто удалить этот вопрос на этом этапе…

Rob 06.08.2024 02:43

Согласитесь, это было бы здорово.

matt 06.08.2024 04:22

Огромное спасибо за помощь @Rob. Я удалил первый.

Sh-RA 06.08.2024 22:31

Другие вопросы по теме