Почему и когда в Swift для EnvironmentObjects допускаются неинициализированные не необязательные значения? - PullRequest
2 голосов
/ 07 июня 2019

В Swift это вылетает во время выполнения:

class EmptyData: BindableObject {
    let didChange = PassthroughSubject<EmptyData, Never>()
}

struct RandomView : View {
    @EnvironmentObject var emptyData: EmptyData
    @EnvironmentObject var emptyData2: EmptyData

    var body: some View {
        Text("Hello World!")
    }
}

и в SceneDelegate.swift:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    let window = UIWindow(frame: UIScreen.main.bounds)
    // The emptyData variables are not initialized as seen below
    window.rootViewController = UIHostingController(rootView: RandomView())
    self.window = window
    window.makeKeyAndVisible()
}

Поток 1: EXC_BAD_INSTRUCTION (код = EXC_I386_INVOP, субкод = 0x0)

Исправить проблему не так сложно, но довольно странно:

window.rootViewController = UIHostingController(rootView: RandomView().environmentObject(EmptyData()))

Так что здесь происходит? Я передаю EmptyData() и SwiftUI решает, что и emptyData, и emptyData2 должны быть инициализированы с одной и той же ссылкой на объект? Я могу также передать другие объекты среды, которые даже не существуют, в качестве переменных в экземпляре RandomView:

window.rootViewController = UIHostingController(rootView: RandomView().environmentObject(EmptyData()).environmentObject(SomeData()))

И SwiftUI просто успешно работает, хотя SomeData() нигде не используется в случае RandomView() и должен вызвать ошибку времени компиляции, на мой взгляд.

Почему неинициализированные значения допускаются во время компиляции без их инициализации при инициализации объекта и почему мы можем свободно передавать экземпляры среды, ничего не делая с ними? Для меня это немного похоже на Javascript, мне понравилась сильная статическая безопасная типизация в Swift ... Я не сразу понимаю, почему инициализатор по элементам просто генерирует инициализатор, который принимает переменные среды в качестве параметра.

Ответы [ 2 ]

1 голос
/ 08 июня 2019

Что такое @EnvironmentObject?

Связанное свойство View, которое читает BindableObject, предоставленное предок

Таким образом, окружающая среда может быть предоставлена ​​детям от предка , необязательно она должна исходить от своего непосредственного родителя. После этого взгляните на приведенный ниже фрагмент, так как RandomViewGrandParent внедряет необходимые объекты Env в среду, RandomViewParent ничего не должен делать, если дочерним элементам RandomViewParent требуется один и тот же объект Env. RandomViewParent может просто инициировать просмотр без повторной передачи env obj.

class EmptyData: BindableObject {
    let didChange = PassthroughSubject<EmptyData, Never>()
}

struct RandomViewGrandParent : View {
    var body: some View {
        RandomViewParent().environmentObject(EmptyData())
    }
}

struct RandomViewParent : View {
    @EnvironmentObject var emptyData: EmptyData
    @EnvironmentObject var emptyData2: EmptyData

    var body: some View {
        RandomView()
    }
}

struct RandomView : View {
    @EnvironmentObject var emptyData: EmptyData
    @EnvironmentObject var emptyData2: EmptyData

    var body: some View {
        Text("Hello World!")
    }
}

А на ваш другой вопрос -

Я передаю EmptyData () и SwiftUI решает, что и emptyData, и emptyData2 должен быть инициализирован с той же ссылкой на объект?

Это потому, что EnvironmentObject соответствует BindableObject, а didChange BindableObject - это Publisher, поэтому я считаю, что и emptyData, и emptyData2 хотят подписаться на одни и те же события / значения, поэтому используют одинаковые ссылки для обоих.

1 голос
/ 08 июня 2019

Делегат свойства EnvironmentObject имеет метод init(), не имеющий параметров, который обеспечивает неявную инициализацию для обернутых свойств

@EnvironmentObject var emptyData: EmptyData
@EnvironmentObject var emptyData2: EmptyData

(это объясняется в видео Modern Swift API Design примерно в 28:10). Вот почему эти (не необязательные) свойства не нуждаются в (явном) начальном значении.

В документации также указывается, что EnvironmentObject (выделение добавлено)

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

Вы должны установить объект модели в представлении предка , вызвав его метод environmentObject (_ :).

Вот как я это понимаю:

  • Если в среде текущего представления или одного из его предков найден соответствующий привязываемый объект (в вашем случае: экземпляр EmptyData), то свойства инициализируются для этого объекта.
  • Если в представлении предка не найдено подходящего связываемого объекта, тогда программа завершается с ошибкой во время выполнения.
  • Объекты среды могут использоваться во всех, некоторых или ни в одном из представлений в иерархии представлений. (См. Поток данных через SwiftUI в 29:20.) Поэтому не является ошибкой предоставление объекта среды (в вашем случае: экземпляр SomeData), который не используется в RandomView.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...