Swift struct 变异变量不起作用? [复制]

Posted

技术标签:

【中文标题】Swift struct 变异变量不起作用? [复制]【英文标题】:Swift struct mutating a variable not working? [duplicate] 【发布时间】:2019-03-01 00:12:24 【问题描述】:

即使在方法中使用 mutating func 关键字,我也无法修改我的模型类变量?

所以基本上我已经用一种非常简单的方式解决了我的问题,我有一个类 Car,它有 3 个变量 idstartmodelNo

之后初始化一个空的 Car 模型数组,然后我想展示 10 辆汽车,创建一个循环的范围 1...10,初始化 Car 模型类并将其附加到原始汽车数组。

检查前 5 辆汽车的 ID 为 0,后 5 辆汽车的 ID 为 1

我想要一个过滤器,其中相同 id 的最后一辆车将启动,所以我创建了一个过滤并修改 start 变量但无法修改它的方法。你能帮我做错什么吗?

请查看我的完整代码并将其复制粘贴到操场上。

struct Car 
    var id = 0
    var start = false
    var modelNo: String

    init(start: Bool, id: Int, model: String) 
        self.start = start
        self.id = id
        self.modelNo = model
    

    mutating func startCar(status: Bool) 
        start = status
    


var arrCars:[Car] = [Car]()

for i in 1...10 
    let md = Car(start: false, id: i <= 5 ? 0 : 1, model: "Model\(i)")
    arrCars.append(md)


private func filtered() 
    for item in arrCars 
        let filteredItems = arrCars.filter  $0.id == item.id 
        if filteredItems.count > 0 
            if var lastItem = filteredItems.last 
                print("Will start \(lastItem.modelNo)")
                lastItem.startCar(status: true)
            
        
    

    let cars = arrCars.filter  (car) -> Bool in
        car.start == true
    

    print(cars.count)


filtered()

任何帮助将不胜感激。

【问题讨论】:

当您mutate 构造实例的属性时,您实际上创建了新实例,其中除了变异属性之外的所有属性都相同,并且变异设置为新值。旧实例保持不变,例如 arrCars 数组中的实例。 但是我想修改arrCars?我该如何解决这个问题? 您应该将arrCars 的内容替换为变异实例。或者在这里使用class 替换内容不是一个好方法,这次我会去上课。期待其他人的其他一些解决方法。谢谢。 我不认为这有什么不同。我要告诉你的是,你想错了。你变异得很好!您只是没有改变与数组中相同的 Car 。请看下面我的回答。当您了解结构是一种值类型时,这很明显。因此重复。 【参考方案1】:

在结构上创建mutating 函数不会改变结构的值语义。一旦结构实例发生变异,就会生成一个副本。

你说:

if var lastItem = filteredItems.last 
    print("Will start \(lastItem.modelNo)")
    lastItem.startCar(status: true)

所以,lastItem 包含您要启动的汽车的实例。在幕后,这与filteredItems 中的汽车实例相同,与arrCars 中的实例相同。但是,一旦你在lastItem 中改变了Car,就会生成一个副本,所以lastItem 不再具有与arrCars 中相同的实例。 filteredItemsarrCars 仍然包含原始的、未更改的 Car 实例。

您可以将Car 更改为class 而不是struct,以便它具有引用语义来获得您想要的行为。

另一种选择是就地修改实例;类似arrCars[0].startCar(status: true)。为此,您需要获取一个数组,其中包含您要启动的汽车的indicies,而不是汽车本身:

private func filtered() 
    for item in arrCars 
        let matchingIndices = arrCars.enumerated().compactMap  (index,car) in
            return car.id == item.id ? index:nil
        
        if matchingIndices.count > 0 
            if let lastIndex = matchingIndices.last 
                print("Will start \(arrCars[lastIndex].modelNo)")
                arrCars[lastIndex].startCar(status: true)
            
        
    

    let cars = arrCars.filter  (car) -> Bool in
        car.start == true
    

    print(cars.count)

但是,如果模型对象需要可变性或有状态,则结构可能不适合。

【讨论】:

@user28434 in cmets 已经说过了吗? 是的,他们在我写答案时发表了评论 好的。感谢您的回答。但是你能详细说明一下变异函数在哪里使用方便吗? 好吧,您可以按照自己的方式使用变异函数,但您需要了解结构的值语义并了解变异结构不会更改任何其他包含以前的值。正如我所解释的,如果您只使用索引而不是实例,您可以使用它直接对数组进行操作。老实说,结构中的变异函数并不是很常见,因为如果你确实需要可变性,你几乎总是需要引用语义 谢谢。很好的解释。【参考方案2】:

到目前为止,您得到的答案并没有错,但它们似乎有点令人困惑。让我们更简单地看一下。问题是这一行:

if var lastItem = filteredItems.last

这会产生一辆汽车为lastItem。但它与filteredItems 中的不一样

那是因为结构是一个值类型。赋值复制结构。因此,您对 lastItem 所做的操作不会影响 filteredItems 内部的内容。

所以,您不会在改变结构时遇到任何问题。你正在变异lastItem 就好了!问题是 lastItem Car 与 filteredItems 中的任何 Car 都不是同一个对象。

【讨论】:

好的。我会处理的。 虽然从语义上讲,您可以想到在进行分配时会发生复制,但从技术上讲,直到发生突变时才会进行复制。 @Paulw11 是的,尽管重要的是要明确这是 Array 的具体实现细节。 Copy-on-write 不是关于结构的事实,而是关于某些高度优化的库类型的事实。 可能。我想我太担心有人纠正我关于 cmets 中的写时复制的问题 :)。我应该只使用描述效果而不是实现的简化解释。【参考方案3】:

如果我们坚持Structures,首先尝试运行这段代码并检查它是否解决了问题。然后我将尝试解释为什么这会有所帮助:

struct Car 
    var id = 0
    var start = false
    var modelNo: String

    init(start: Bool, id: Int, model: String) 
        self.start = start
        self.id = id
        self.modelNo = model
    

    mutating func startCar(status: Bool) 
        start = status
    


var arrCars: Array<Car> = [Car]()

for i in 1...10 
    let md = Car(start: false, id: i <= 5 ? 0 : 1, model: "Model\(i)")
    arrCars.append(md)


private func filtered() 
    for item in arrCars 
        let filteredItems = arrCars.filter  $0.id == item.id 
        if filteredItems.count > 0 
            // solves the problem
            arrCars[filteredItems.count-1].startCar(status: true)
            print("Will start \(arrCars[filteredItems.count-1].modelNo)")
        
    

    let cars = arrCars.filter  (car) -> Bool in
        car.start == true
    

    print(cars.count)


filtered()

【讨论】:

没有。现在cars 数组中只有一辆车。我想要两个值,最后一个打印语句将打印 2 以获得正确的输出。 @Vishal16,对不起,我没有完全理解代码。您是否注意到,在提出更改之前,程序会打印 0。更改后会打印 1。它对结构进行了必要的修改。 我只是说有两辆车将启动,而在您的解决方案中只有 1 辆车启动了。 @Vishal16,感谢您的回复。但我还是很困惑。我理解的代码只启动最后一辆车还是我错了? 好的,现在知道了。谢谢!似乎不需要进一步修改答案,因为 Paulw11 已经提供了我打算提供的代码版本。根据我的理解,关键是 Swift 中的数组本身就是结构。并且可以通过下标[] 运算符使用来修改它的项目。这是一个有趣的问题,恕我直言,不会重复附加的问题。祝你好运!

以上是关于Swift struct 变异变量不起作用? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

闭包中的Swift可变结构和结构的行为不同

vuex 未知本地变异类型:updateValue,全局类型:app/updateValue。突变不起作用

在 Swift 中带有变量的 NSLocalizedString 不起作用

我可以将相关对象添加到Swift Struct吗?

JavaScript 中的简单变异观察者示例不起作用

Swift:将 Facebook JSON 中的名称、照片和封面设置为变量不起作用。