Usar @Observable
o modelo de exibição com uma coleção de matrizes, se abordado no init, acionará um segundo ciclo de vida do aplicativo quando sofrer mutação.
Quebrando:
Raiz :
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
MainContentView()
}
}
}
Modelo de visualização
@Observable
class ContentViewModel {
var items = [String]()
init() {
print("Init")
if items.isEmpty {}
}
deinit {
print("deinit")
}
func update() {
items.append("New")
}
}
Visualizar :
struct ContentView: View {
@State var viewModel: ContentViewModel = .init()
var body: some View {
Button("Update", action: viewModel.update)
}
}
Ao executar isso, ele imprimirá "Init" (esperado, é claro), mas clicar em "Update" aciona um ciclo de vida completo.
Ele imprimirá outro "Init". Definir um ponto de interrupção no (único) WindowGroup
parará ali. O que significa (eu acho) que o todo var body: some Scene
está sendo acionado.
- Simplesmente removendo
items.isEmpty
, o problema desaparece. - O gráfico de memória está mostrando o
ContentViewModel
dobro. - Não há definição.
- Só acontece com
@Observable
(não acontece se usarObservableObject/ObservedObject
)
A principal diferença entre
@StateObject
e@State
é que o inicializador de propriedade de@State
é chamado toda vez que o inicializador da visualização é chamado.Então, depois que
viewModel
ele é inicializado pela primeira vez, você acaba criando uma instância temporária paraContentViewModel
cada chamada subsequenteContentView.init
.Normalmente, a instância temporária é imediatamente desinicializada, mas ainda pode afetar o desempenho se você estiver fazendo algo pesado no
@Observable
inicializador da classe. Isso é apontado na documentação paraState
.Às vezes, uma instância extra é retida por causa de algumas peculiaridades no algoritmo do SwiftUI de criação de um gráfico de dependência. Pela minha experiência, só vi no máximo uma instância extra por
@State
.No seu caso, a linha
if items.isEmpty {}
acessa a propriedade observed-trackeditems
, e esse acesso é registrado noobservationRegistrar
. Isso afeta o gráfico de dependência do SwiftUI de uma forma ou de outra, e a instância não é liberada.Você pode resolver isso fazendo o que a documentação recomenda: tornando-o opcional e inicializando-o em
task
ouonAppear
.Alternativamente, você pode usar este wrapper de propriedade do malhal .
Este wrapper de propriedade encapsula a expressão inicializadora de propriedade em um
@autoclosure
, para que ele atue de forma semelhante a@StateObject
.