#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 внутри материала ? @мэтт