Переменная, используемая перед инициализацией ошибка (Swift)

#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 или подобного, размещенного в родительском представлении.