#swift #core-data #swiftui
Вопрос:
Я продолжаю получать сообщение об ошибке/ворсе, которое гласит Variable 'self.item' used before being initialized
. Это сообщение появляется только тогда, когда я, по-видимому, добавляю @State
тип Date
(см. Строку с комментариями ниже).
Переменная item
-это CoreData
значение, которое я пытаюсь обновить с помощью формы. Все остальные необходимые типы данных (int, строка, данные и т.д.) Работают должным образом.
Я вполне уверен, что это проблема, которая проистекает из моего отсутствия опыта работы с языками Swift или декларативного стиля в целом, но я также опасаюсь, что это может быть проблемой компилятора, так как я, кажется, сталкиваюсь с некоторыми из них.
import SwiftUI
struct SelectionView: View {
@State var item : Item
@Environment(.managedObjectContext) private var viewContext
@State private var name : String
@State private var imageData : Data
@State private var timestamp : Date // error only appears when this line is added
init(item : Item) {
self.item = item
self._name = State(initialValue: item.name!)
self._imageData = State(initialValue: item.imageData!)
self._timestamp = State(initialValue: item.timestamp!)
}
var body: some View {
VStack {
Form
{
TextField("Name", text: $name)
Image(uiImage: UIImage(data: imageData)!)
Button(action: changeImage, label: { Text("Change image") })
Button(action: save, label: { Text("Save") })
}
}
.padding()
}
...
Комментарии:
1. Вы объявили дату и пытаетесь назначить метку времени. Используйте одно и то же время данных для @State var
self._timestamp = State(initialValue: Date(timeIntervalSince1970: item.timestamp))
2. Спасибо за ваш комментарий. Почему это не относится к имени или данным изображений?
3. Вам действительно нужны 3 другие переменные состояния? Вы могли бы просто использовать предмет.
$item.name
,item.imageData
4. Специально для основных данных я бы использовал
Observable
прокси-класс с методами from и to в расширенииNSManagedObject
подкласса. Преимуществом является более удобная обработка дополнительных опций, и вам не нужно вставлять новый объект в контекст, если вы также хотите использовать форму для добавления нового элемента.5. @vadian не могли бы вы указать мне на некоторые онлайн-ресурсы, которые демонстрируют это? Я всегда стремлюсь улучшить свой код в соответствии с наилучшей возможной практикой, поэтому буду очень признателен за любую помощь.
Ответ №1:
Просто сделайте следующее:
@State var item: Item
init(item: Item) {
_item = State(initialValue: item)
}
Вы можете получить доступ ко всем вещам, которые вам могут понадобиться:
item
$item.name
(эквивалентно тому , когда у вас было$name
, то же самое для других ниже)$item.imageData
$item.timestamp
Кроме того, если вам не нужно передавать что-либо еще, вы также можете избавиться от инициализатора, потому что это подразумеваемый инициализатор по элементам.
Причина ошибки, с которой вы столкнулись:
self.item
еще не существует. Вы устанавливаете значение @State
with _item
, и это сделает переменные item
and $item
.
Мы можем видеть это, потому что просмотр определения State
показывает следующее:
public var wrappedValue: Value { get nonmutating set } // Normal value
/* ... */
public var projectedValue: Binding<Value> { get } // Binding value
Поэтому, поскольку вы не можете получить доступ self.item
, потому _item
что он еще не был инициализирован, вы получаете сообщение об ошибке. Вот почему мы установили _item
использование State(initialValue:)
.
Комментарии:
1. Спасибо вам за ответ. Однако у вас есть какие-либо идеи, почему мое решение приводит к этой конкретной ошибке?
2. @Dan Добавил некоторые пояснения.
3. Часто в инициализаторе вы обнаружите, что порядок инициализации имеет значение для компилятора. Тем не менее, вы должны принять приведенный выше ответ, поскольку это наиболее правильное и элегантное решение. Вы не должны объявлять 4 переменные состояния там, где это будет делать одна.
4. Спасибо. Отличный ответ, и это поможет мне разрабатывать лучшие приложения в будущем.
5. Хотя вы можете сделать это
_item = ...
с помощью оболочек свойств, это считается анти-шаблоном, если вы инициализируете@State
переменную в инициализаторе представления SwiftUI. Это утверждение не будет иметь никакого эффекта, если соответствующее хранилище для состояния для «концептуального представления» уже инициализировано. Обратите внимание, что время жизни представления структуры не привязано к состоянию, а к его идентичности — или «концептуальному представлению» с той же идентичностью. Правильный способ использовать «внешние» значения-передавать их в качестве привязок, исходное значение которых обычно берется из @ObservableObject или подобного, размещенного в родительском представлении.