Уведомления не отправляются при изменении объекта наблюдения

#swift #swiftui #combine #observableobject

Вопрос:

У меня есть следующий код:

 class Stuff {  var str: String?  var num = 0 }  class MyStuff:ObservableObject {  @Published var stuff:Stuff?  @Published var numer: Int? }  class DoSomething {  let observedObject = MyStuff()  var cancellableBag = Setlt;AnyCancellablegt;()    init() {  observedObject.objectWillChange.sink { value in  print(value)  print("Object has changed")  }.store(in: amp;cancellableBag)  observedObject.$stuff.sink { value in  print(value?.str ?? "")  }.store(in: amp;cancellableBag )  } }  

Но когда я выполню:

 let doSomething = DoSomething() doSomething.observedObject.stuff?.str = "Doing something" doSomething.observedObject.stuff?.num = 2  

, уведомления никогда не срабатывают:

введите описание изображения здесь

Кто-нибудь из вас знает, почему уведомления никогда не срабатывают? Или как я могу это сделать?

Комментарии:

1. Измените свое Stuff » от class «на struct «.

2. @OOPer, почему это имеет значение? Это потому, что мутация экземпляра класса не устанавливает ссылку?

3. @matt Значение MyStuff , опубликованное в stuff свойстве, является указателем на объект. Уведомление @ObservableObject будет отправлено только в том случае, если этот указатель будет заменен. Свойства объектов, находящихся за этим указателем, не наблюдаются, только само значение указателя.

Ответ №1:

Как уже предлагалось в комментариях, вам нужно преобразовать свой класс в структуру, если вы хотите, чтобы он работал: struct Stuff { . Структура-это тип значения, с которым она хорошо работает ObservableObject , в то время как класс-это ссылочный тип, который, ну, не так хорошо работает.

@Published свойства должны быть «чистыми» типами значений, если вы хотите получать уведомления, когда что-то в их содержимом меняется. Под «чистым» я подразумеваю типы значений, которые состоят только из других типов значений, которые также являются «чистыми».

Зачем это нужно? Это связано с тем, что @Published механизм полагается на willSet didSet наблюдателей свойств/, а наблюдатели свойств выполняются только при изменении значения, которое они содержат.

При использовании типов значений любое изменение в их структуре распространяется вверх по потоку, в результате чего обновляется все свойство. Это происходит из-за семантики типов значений.

Смотрите этот фрагмент кода:

 struct Question {  var title: String = "" }  class Page {  var question = Question() {  didSet {  print("Question changed: (question)")  }  } }  let page = Page() page.question.title = "@Published doesn't work"  

Любое изменение question элементов приводит к замене всего свойства, что запускает наблюдателей willSet / didSet свойств, поэтому вы можете легко отслеживать любые изменения в структуре данных question .

Однако вышесказанное не относится к классам. Классы имеют идентичность, они имеют фиксированное место в памяти, в отличие от типов значений, которые при использовании копируются повсюду. Это означает, что изменения в их внутренней структуре не отражаются выше по потоку, так как хранилище экземпляров класса не изменяется.

Единственный раз @Published , когда наблюдатель запускается для классов, — это когда вы заменяете сам объект. Попробуйте это, и вы увидите, что уведомления будут отправлены:

 doSomething.observedObject.stuff = Stuff() // this will print `Object has changed`  

Ответ №2:

Проблема заключалась doSomething.observedObject.stuff в том, что он был равен нулю.

Я исправил:

 let doSomething = DoSomething() let stuff = Stuff() stuff.str = "Doig Something" doSomething.observedObject.stuff = stuff  

Комментарии:

1. Нет, вы упустили главное, потому что, если вы сейчас скажете doSomething.observedObject.stuff?.str = "Doing something else" , что все равно не получите «уведомление».

2. @мэтт, ты прав. Вы знаете, как это можно исправить?

3. Вы читали комментарии? Вам уже дважды говорили ответ.

4. Я понимаю, это потому, что мутация экземпляра класса не устанавливает ссылку. Но мой вопрос в том, как сделать ссылку на str внутри материала ? @мэтт