У меня есть следующий код, который я использую со 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
}
}
Однако я получаю сообщение об ошибке:
Экранирующее закрытие захватывает мутирующий параметр «я»
Я понимаю, что изменение структуры на класс решит эту проблему, но есть ли способ фактически зафиксировать мутирующее «я» в ускользающем замыкании в структуре?
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.
struct
, заключается в том, что я не смог изначально настроить решение ObservableObject
.
Решение, с которым я закончил:
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
}
}
}
Похожие вопросы
Новые вопросы
ios
iOS - мобильная операционная система, работающая на Apple iPhone, iPod touch и iPad. Используйте этот тег [ios] для вопросов, связанных с программированием на платформе iOS. Используйте связанные теги [target-c] и [swift] для проблем, характерных для этих языков программирования.