【问题标题】:didSet not called by Array.append() in SwiftSwift 中的 Array.append() 未调用 didSet
【发布时间】:2020-03-29 12:22:55
【问题描述】:

我正在关注100 Days of SwiftUI 并已到达Day 37。在做Making changes permanent with UserDefaults的时候,遇到didSet的问题。

(我在 iOS 13.4 中使用 Swift 5)

在示例代码中,它写道

.navigationBarItems(trailing: Button("Save") {
    if let actualAmount = Int(self.amount) {
        let item = ExpenseItem(name: self.name, type: self.type, amount: actualAmount)
        self.expenses.items.append(item)
    }
})

didSet 应由 .append() 调用。

然而,在实践中,didSet 不会被调用,除非我将上面的代码更改为

.navigationBarItems(trailing: Button("Save") {
    if let actualAmount = Int(self.amount) {
        let item = ExpenseItem(name: self.name, type: self.type, amount: actualAmount)
        let newItems = self.expenses.items + [item]
        self.expenses.items = newItems
    }
})

我还在 Playground 中编写了一个小测试(见下文),它表明 .append()didSet 配合得很好

struct Count {
    var array: [Int] {
        didSet {
            print("struct Count - didSet() called")
        }
    }
}
class CountClass {
    var array: [Int] {
        didSet {
            print("class CountClass - didSet() called")
        }
    }
    init() {
        array = [1, 2, 3]
    }
}
struct Test {
    var countA = Count(array: [1, 2, 3])
    var countB = CountClass()

    mutating func testDidSet() {
        countA.array.append(4)
        countB.array.append(4)
    }
}

var t = Test()
t.testDidSet()

这种奇怪的行为真的让我想知道didSet 是如何工作的。还是这个问题与@ObservedObject的使用有关(示例项目就是这种情况)?

PS:我从Project7下载了汉化版,也有问题。

【问题讨论】:

  • 状态或发布的属性包装器使用“将设置”行为。
  • @Chris 它不是重复的,尽管我同意这是对 SwiftUI 工作原理的相同误解。
  • @user3441734 我将didSet 更改为willSet,仍然无法正常工作......我还尝试将整个ExpenseItemExpenses 更改为一个虚拟的Int 值包裹在另一个虚拟类,它适用于=。这真的让我想知道didSetwillSet 是否只适用于= ...叹息...
  • 不要走错路!您已经使用过 willSet 和 didSet 属性观察器。使用 ObservedObject 属性包装器没有“奇怪的行为”或一些“相关的问题”。尽量避免混合不同的策略(至少在开始时)并遵循一些 Apple 教程。 SwiftUI 是一个新概念,一个新范式。大多数麻烦(即使是经验丰富的 swift 程序员也可能经常遇到)是缺少文档

标签: swift append swiftui didset


【解决方案1】:

这是一个已知的 Swift 5.2 错误:包装属性的观察者不会在修改时被调用 (https://bugs.swift.org/browse/SR-12089)。他们从一月份就知道了这一点,并且同样发布了一个更新,破坏了一堆生产代码¯\_(ツ)_/¯

问题中已经提出了一个临时解决方法 - 属性重新分配而不是修改。

【讨论】:

    【解决方案2】:

    我也经历了这个项目。确保Expenses 类中的items 属性标记为@Published。如下;

    import SwiftUI
    
    struct ExpenseItem: Identifiable, Codable {
    let id = UUID()
    let name: String
    let type: String
    let amount: Int
    }
    
    class Expenses: ObservableObject {
    init() {
        if let items = UserDefaults.standard.data(forKey: "Items") {
            let decoder = JSONDecoder()
            if let decoded = try? decoder.decode([ExpenseItem].self, from: items)     {
                self.items = decoded
                return
            }
        }
        self.items = []
    }
    
    
    @Published var items: [ExpenseItem] {
        didSet {
            let encoder = JSONEncoder()
            if let encoded = try? encoder.encode(items) {
                UserDefaults.standard.set(encoded, forKey: "Items")
            }
        }
    }
    }
    

    并在导航栏中的“保存”按钮中添加self.presentationMode.wrappedValue.dismiss()

    .navigationBarItems(trailing: Button("Save") {
                if let actualAmount = Int(self.amount) {
                    let item = ExpenseItem(name: self.name, type: self.type,   amount: actualAmount)
                    self.expenses.items.append(item)
                    self.presentationMode.wrappedValue.dismiss()
                } else {
                    self.isShowingAlert = true
                }
    
            })
    

    【讨论】:

    • 我已经按照教程完成了整个项目,但我认为@Publilshself.presentationMode.wrappedValue.dissmiss() 不会解决问题。发布在 Github 上的最终项目也失败了......
    【解决方案3】:

    我想我上周完成了这个项目,但是查看Swift GitHub page 似乎 24 号有更新。

    当我做这个项目时,一切都很好,但现在我在第 47 天,我遇到了这个问题。可能和 Swift 5.2 更新有关。

    【讨论】:

    • 我不得不说你有点毁了我的一天,因为我在第 46 天并且即将进入第 47 天……真的很讨厌必须做一些“错误”的事情修复错误...
    【解决方案4】:

    发生的事情是 Swift 没有将 .append() 识别为设置变量。您可以通过将数组复制到一个新的临时数组、附加新值然后将您的类数组设置到这个临时数组来解决这个问题。

    var newActivityList = [Activity]() //new temporary array
    for activity in self.activityList.activities {
        newActivityList.append(activity) //copy class array to temp array
    }
    newActivityList.append(newActivity) //append new value
    
    self.activityList.activities = newActivityList //set class array to temp array
    

    PS:上面的例子是第47天,但逻辑是一样的。

    【讨论】:

      猜你喜欢
      • 2023-04-11
      • 2018-02-02
      • 2020-01-17
      • 1970-01-01
      • 2020-03-30
      • 2017-06-18
      • 2014-07-23
      • 2017-03-30
      • 1970-01-01
      相关资源
      最近更新 更多