У меня есть следующий код, который я использую со SwiftUI:

import Foundation

public struct Trigger {
    public var value = false

    public mutating func toggle() {
        value = true
        let responseDate = Date().advanced(by: 3)

        OperationQueue.main.schedule(after: .init(responseDate)) {
            moveBack()
        }
    }

    private mutating func moveBack() {
        value = false
    }
}

Однако я получаю сообщение об ошибке:

Экранирующее закрытие захватывает мутирующий параметр «я»

enter image description here

Я понимаю, что изменение структуры на класс решит эту проблему, но есть ли способ фактически зафиксировать мутирующее «я» в ускользающем замыкании в структуре?

0
Richard Topchii 10 Янв 2022 в 10:38
1
Нет. Модели просмотра почти всегда должны быть классами, и они в значительной степени должны быть, если вы хотите изменить модель.
 – 
Paulw11
10 Янв 2022 в 10:44
Спасибо!........
 – 
Richard Topchii
10 Янв 2022 в 10:49

2 ответа

Лучший ответ

Как вы уже поняли, быстрое решение — использовать ссылочный тип, класс. Но почему это так?

Структуры Swift являются типами значений, поэтому они неизменяемы. Вы можете пометить функцию как mutating, чтобы указать компилятору, что функция изменяет структуру, но что это на самом деле означает?

Рассмотрим простую структуру:

struct Counter {
   var count

   init(_ count: Int = 0)
   {
       self.count = count
   }

   mutating func increment() {
       self.count+=1
   }
}

Теперь попробуйте присвоить экземпляр этого константе let:

let someCounter = Counter()
someCounter.increment()
print(someCounter.count)

Вы получите сообщение об ошибке; вам нужно использовать var.

var someCounter = Counter()
someCounter.increment()
print(someCounter.count)

Что на самом деле происходит, когда вы вызываете функцию mutating, так это то, что создается новый Counter с новым count, и он назначается someCounter . Это эффективно говорит someCounter = Counter(someCounter.count+1)

А теперь подумайте, что произойдет, если вы сможете изменить self в ускользающем замыкании. Этот новый Counter будет создан в некое неопределенное время в будущем, но выполнение уже продвинулось. Слишком поздно обновлять someCounter.

Другое преимущество использования class, как вы обнаружили, заключается в том, что вы можете использовать ObservableObject, что значительно упрощает обновление представлений SwiftUI.

2
Paulw11 10 Янв 2022 в 11:01
Спасибо за очень подробное объяснение. Единственная причина, по которой я начал изучать решение struct, заключается в том, что я не смог изначально настроить решение ObservableObject.
 – 
Richard Topchii
10 Янв 2022 в 11:07

Решение, с которым я закончил:

import Foundation
import Combine

public final class IntervalTrigger: ObservableObject {
    private let timeInterval: TimeInterval
    @Published var value = false

    public init(_ timeInterval: TimeInterval) {
        self.timeInterval = timeInterval
    }

    public func toggle() {
        value = true
        let responseDate = Date().advanced(by: timeInterval)
        OperationQueue.main.schedule(after: .init(responseDate)) { [weak self] in
            self?.value = false
        }
    }
}

0
Richard Topchii 10 Янв 2022 в 10:50