SwiftUI - источник данных как структура - производительность и избыточность посредством копий - зачем все еще использовать его? - PullRequest
0 голосов
/ 29 марта 2020

У меня есть минимальный рабочий пример чего-то, в чем я до сих пор не уверен:

import SwiftUI

struct Car {
    var name: String
}

class DataModel: ObservableObject {
    @Published var cars: [Car]

    init(_ cars: [Car]) {
        self.cars = cars
    }
}

struct TestList: View {
    @EnvironmentObject var dataModel: DataModel

    var body: some View {
        NavigationView {
            List(dataModel.cars, id: \.name) { car in
                NavigationLink(destination: TestDetail(car: car).environmentObject(self.dataModel)) {
                    Text("\(car.name)")
                }
            }
        }
    }
}

struct TestDetail: View {
    @EnvironmentObject var dataModel: DataModel
    var car: Car

    var carIndex: Int {
        dataModel.cars.firstIndex(where: {$0.name == self.car.name})!
    }

    var body: some View {
        Text(car.name)
            .onTapGesture {
                self.dataModel.cars[self.carIndex].name = "Changed Name"
        }
    }
}

struct TestList_Previews: PreviewProvider {
    static var previews: some View {
        TestList().environmentObject(DataModel([.init(name: "A"), .init(name: "B")]))
    }
}

Речь идет об использовании структур в качестве моделей данных. Пример похож на официальный учебник Apple по SwiftUI (https://developer.apple.com/tutorials/swiftui/handling-user-input).

По сути, у нас есть класс DataModel, который передается по дереву как EnvironmentObject , Класс охватывает базовые типы данных c нашей модели. В данном случае это массив структуры Car:

class DataModel: ObservableObject {
    @Published var cars: [Car]
    ...
}

. Пример состоит из простого списка, в котором указаны названия всех автомобилей. Когда вы нажимаете на один, вы попадаете в подробный вид. Подробному представлению передается свойство car как (в то время как dataModel передается как EnvironmentObject):

NavigationLink(destination: TestDetail(car: car).environmentObject(self.dataModel)) {
    Text("\(car.name)")
}

Свойство car подробного представления используется для его заполнения. Однако, если вы хотите, например, изменить имя car в подробном представлении, вам нужно от go до dataModel, поскольку car - это просто копия исходного экземпляра, найденного в dataModel. Таким образом, сначала вы должны найти индекс автомобиля в массиве cars dataModel, а затем обновить его:

struct TestDetail: View {
    ...
    var carIndex: Int {
        dataModel.cars.firstIndex(where: {$0.name == self.car.name})!
    }
    ...
    self.dataModel.cars[self.carIndex].name = "Changed Name"

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

Кроме того, это означает, что вы есть повторяющиеся данные. Свойство car подробного вида точно отражает car viewModel. Это разделяет данные. Это не правильно.

Если бы car было class вместо struct, это не было бы проблемой, потому что вы передаете экземпляр как ссылку. Было бы намного проще и чище.

Однако, похоже, что каждый хочет использовать структуры для этих вещей. Конечно, они безопаснее, с ними не может быть циклов ссылок, но это создает избыточные данные и вызывает более дорогие операции. По крайней мере, для меня это выглядит так.

Мне бы очень хотелось понять, почему это вообще может не быть проблемой и почему на самом деле это лучше, чем уроки. Уверен, у меня просто проблемы с пониманием этого как новой концепции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...