Каковы преимущества и недостатки ручного вызова objectWillChange.send() в SwiftUI?

Ответ

objectWillChange.send() — метод ObservableObject протокола для ручного уведомления SwiftUI об изменениях.

Преимущества:

  • Гибкость для сложных свойств: Позволяет уведомлять об изменениях вычисляемых свойств или свойств, которые не могут быть помечены как @Published.
  • Групповые обновления: Можно объединить несколько изменений в одно уведомление для оптимизации.
  • Работа с внешним кодом: Полезно при интеграции с не-SwiftUI кодом, который меняет состояние объекта.

Недостатки:

  • Нет точности: SwiftUI перерисовывает все представления, зависящие от этого объекта, а не только от конкретного свойства.
  • Снижение прозрачности: Усложняет понимание потока данных по сравнению с явными @Published свойствами.
  • Риск избыточных обновлений: Легко вызвать уведомление, даже когда данные фактически не изменились.
  • Обход механизмов SwiftUI: Может мешать работе @StateObject или @EnvironmentObject.

Рекомендации и пример: Используйте @Published по умолчанию. Применяйте objectWillChange.send() только для специальных случаев.

class SettingsViewModel: ObservableObject {
    // Стандартное использование @Published
    @Published var username: String = ""

    // Сложное свойство, требующее ручного уведомления
    private var internalScore = 0
    var score: Int {
        get { internalScore }
        set {
            // Производим валидацию или логику
            if newValue != internalScore && newValue >= 0 {
                internalScore = newValue
                // Ручное уведомление об изменении
                objectWillChange.send()
            }
        }
    }

    // Групповое обновление
    func resetAll() {
        username = ""
        internalScore = 0
        // Одно уведомление вместо двух
        objectWillChange.send()
    }
}

Ответ 18+ 🔞

Да ты посмотри, какая интересная хуйня! Вот этот objectWillChange.send() — он как запасной аэродром в протоколе ObservableObject. Ну знаешь, когда всё по красоте через @Published не получается.

Чем он хорош, этот ручной костыль?

  • Для мозговыносящих свойств: Допустим, у тебя там вычисляемое свойство, которое пляшет с бубном, или геттер/сеттер с логикой. Воткнул objectWillChange.send() в сеттер — и всё, SwiftUI в курсе дела.
  • Чтоб не дрочить вьюхи по десять раз: Сделал кучу изменений в модели — и один раз крикнул objectWillChange.send(). Одно обновление вместо десяти, красота.
  • Когда снаружи лезет старый код: Какая-нибудь legacy-хрень на Objective-C плюёт в твой объект. Ты ловишь этот плевок и руками отправляешь уведомление: «Эй, SwiftUI, смотри-ка, что тут у нас поменялось!».

А чем он говён?

  • Точности — ноль ебать: Ты кричишь «ОБНОВЛЯЙСЯ!», а SwiftUI, как дурак, перерисовывает ВСЁ, что за этим объектом следит. Даже если изменилась одна мелкая хуйня.
  • Прозрачность накрылась медным тазом: Глядишь в код — и хуй поймёшь, от чего именно вьюха перерисовывается. Где @Published — там всё ясно, а тут — загадка, блядь.
  • Задрочить можно по-страшному: Забыл проверить, а поменялось ли значение на самом деле? Отправил уведомление на ровном месте — и пошла цепочка лишних ререндеров. Пиздец оптимизации.
  • Можно сломать родные механизмы: Начинаешь рулить уведомлениями сам — и @StateObject с @EnvironmentObject могут посмотреть на тебя как на идиота и перестать работать как надо.

Так что делать-то, гений? Да хуй его знает... Шучу. По умолчанию — всегда @Published. Это святое. А objectWillChange.send() — это как аварийный молоток в стекле. Бьёшь только когда припёрло.

Вот, смотри, как это бывает в жизни:

class SettingsViewModel: ObservableObject {
    // Ну тут всё стандартно, красота
    @Published var username: String = ""

    // А вот свойство с подвохом
    private var internalScore = 0
    var score: Int {
        get { internalScore }
        set {
            // Тут у нас логика, валидация, ебатория
            if newValue != internalScore && newValue >= 0 {
                internalScore = newValue
                // Ага, изменилось! Кричим вручную!
                objectWillChange.send()
            }
        }
    }

    // Функция-убийца, сбрасывает всё
    func resetAll() {
        username = ""
        internalScore = 0
        // И вместо двух отдельных воплей — один мощный рёв!
        objectWillChange.send()
    }
}

В общем, инструмент как инструмент. Главное — не начать им всё подряд забивать гвозди, а то потом разбираться будет овердохуища.