SwiftUI Model Class VS Struct

158 阅读3分钟

为啥提了这个问题

SwiftUI 中的 MVVM 大部分讨论的是VM ,但是对于Model 的可变 和不可变的问题还是有点问题没有说的很明确。 最近遇到一个问题,在对于VM的数据处理部分,我看到老代码很多地方对于Array的数据里面一个 数据的Model的 具体属性的Updata,特别是不涉及到array 的数据number 变化的情况下。

  • 1 发现 用Struct的Model 的确有 array remvoe & insert 的情况
  • 2 在MainThread 里面进行因为 VM 的list是@Publsh
  • 3 卡顿

这一切的问题在于Model是Struct ,哪怕VM 已经是一个StateObject

Class 版本,代码如图,同样的疑问

[stackoverFlow](stackoverflow.com/questions/5…)


import Foundation
import SwiftUI
import Combine


class Person: Equatable, Hashable ,Identifiable,ObservableObject{
    var id = UUID()
    @Published var name: String = ""
    
    static func == (lhs: Person, rhs: Person) -> Bool {
        lhs.id == rhs.id
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
        hasher.combine(name)
    }
  
    init(name: String) {
        self.name = name
    }
}

class VModel: ObservableObject{
    
    @Published var people: [Person]

    init(){
        self.people = [
            Person(name:"Juan"),
            Person(name:"Pedro"),
            Person(name:"Luis"),
            Person(name:"Javier1"),
        ]
    }
}

struct ListRow: View {
    
    @ObservedObject var people : Person
    
    var body: some View {
        HStack {
            Text("\(people.name)")
            Spacer()
            Button(action: {
                people.name = people.name + "1"
            }, label: {
                Text("Change Name")
            })
        }
        
        
    }
}

struct ContentView1: View {
    @StateObject var vmodel = VModel()

    var body: some View {
        VStack{
            List {
                ForEach(vmodel.people ,id: \.self){ person in
                     ListRow(people: person)
                }
            }
        }
        .environmentObject(vmodel)
    }
}


#Preview {
    ContentView1()
}

Struct

如果model 是struct 那就必须 自己去寻找对应的 ViewModel 里面的数据 var 变量 这样的有很多不方便的地方,在子view里面必须把VM 给带上,没有很好的利用swiftUI 的关键字 @ObservableObject ,@environmentObject 等一系列组合


struct ContentView: View {
   @StateObject var vm = FileViewModel()
   var body: some View {
       HStack {
           Button(action: {
               vm.changeOneName(index: 2, name: "hahahh ")
           }, label: {
               Text("改名1 ")
           })
           Button(action: {
               vm.changeOneName(index: 3, name: "hahahh ")
           }, label: {
               Text("改名2 ")
           })
       }
       List {
           Section {
               ForEach(self.vm.files) { file  in
                   Text("\(file.name)")

               }
           } header: {
               Text("\(self.vm.files.count)")
           }
            
       }
       .padding()
       .onAppear {
           for i in 0..<100 {
               let f = File(name: "\(i)", path: "path is \(i)")
               vm.files.append(f)
           }
           
           
       }
   }
       
}
extension Array {
   subscript(safe range: Range<Index>) -> ArraySlice<Element> {
       if range.endIndex > endIndex {
           if range.startIndex >= endIndex {
               return []
           } else {
               return self[range.startIndex..<endIndex]
           }
       } else {
           return self[range]
       }
   }
}

#Preview {
   ContentView()
}

struct ContentView: View {
   @StateObject var vm = FileViewModel()
   var body: some View {
       HStack {
           Button(action: {
               vm.changeOneName(index: 2, name: "hahahh ")
           }, label: {
               Text("改名1 ")
           })
           Button(action: {
               vm.changeOneName(index: 3, name: "hahahh ")
           }, label: {
               Text("改名2 ")
           })
       }
       List {
           Section {
               ForEach(self.vm.files) { file  in
                   Text("\(file.name)")

               }
           } header: {
               Text("\(self.vm.files.count)")
           }
            
       }
       .padding()
       .onAppear {
           for i in 0..<100 {
               let f = File(name: "\(i)", path: "path is \(i)")
               vm.files.append(f)
           }
           
           
       }
   }
       
}
extension Array {
   subscript(safe range: Range<Index>) -> ArraySlice<Element> {
       if range.endIndex > endIndex {
           if range.startIndex >= endIndex {
               return []
           } else {
               return self[range.startIndex..<endIndex]
           }
       } else {
           return self[range]
       }
   }
}

#Preview {
   ContentView()
}

感觉 AI 说的对

AI : When it comes to SwiftUI, both classes and structs can be used as model types. However, there are some differences between them.

  1. Reference vs Value:

    • Classes are reference types, meaning that when you pass a class instance around, you are passing a reference to the same instance.
    • Structs are value types, meaning that when you pass a struct instance around, you are passing a copy of the instance.
  2. Mutability:

    • Classes can be mutable or immutable. You can change the properties of a class instance even if it is declared as a constant (using let).
    • Structs are immutable by default. If you want to modify the properties of a struct instance, you need to mark it as var instead of let.
  3. Inheritance:

    • Classes support inheritance, allowing you to create a hierarchy of classes with shared properties and behaviors.
    • Structs do not support inheritance. They cannot be subclassed.

In general, if you need reference semantics, inheritance, or mutability, you may prefer using classes. On the other hand, if you prefer value semantics, immutability, and simpler data structures, structs can be a good choice. The decision between classes and structs ultimately depends on the specific requirements and design of your SwiftUI application.

但是对于 情况,AI 没有举出很好例子。我自己当了回ai注解

引用: