在 Swift 中传递值类型作为引用

Posted

技术标签:

【中文标题】在 Swift 中传递值类型作为引用【英文标题】:Passing a value type as reference in Swift 【发布时间】:2017-09-10 00:25:33 【问题描述】:

我的模型在 Swift 3.0 中实现为 structs。其中几个structs 具有应该能够根据用户的操作修改模型的委托。

但是,当我将 struct 传递给 delegate 方法时,它会被复制。

你如何解决这个问题?您能否强制编译器将此struct 作为参考传递,或者唯一的选择是使用class

【问题讨论】:

首先使用struct 的全部意义在于这是一种可取的行为。它保留了数据的不变性。 inout 可以做到这一点,但一般情况下不推荐。为什么复制是一个问题? 那你如何从Controller修改模型呢?如果您有一个委托方法将模型作为第一个参数传递,就像在 Cocoa 中一样,委托(通常是控制器)如何修改 real 模型,而不是它的副本? 有些相关:How to use a value type object as a reference type? @cfischer 修改模型是控制器的工作,而不是委托。控制器应该询问委托如何修改模型,然后自己修改它。 【参考方案1】:

首先使用 struct 的全部意义在于这是一种可取的行为。它保留了数据的不变性。 inout 可以实现,但一般情况下不推荐。

protocol Delegate 
    func callback(_ oldValue: Int) -> Int


struct IncrementerDelegate: Delegate 
    let step: Int

    func callback(_ oldValue: Int) -> Int 
        return oldValue + step
    


struct Model 
    var i = 0


class Controller 
    var model = Model()
    var delegate: Delegate

    init(delegate: Delegate) 
        self.delegate = delegate
    

    // To be called externally, such as by a button
    func doSomething() 
        // Delegate determains new value, but it's still the
        // controller's job to perform the mutation.
        model.i = delegate.callback(model.i)
    


let delegate = IncrementerDelegate(step: 5)
let controller = Controller(delegate: delegate)
print(controller.model.i)
controller.doSomething() // simulate button press
print(controller.model.i)








protocol CrappyDelegate 
    func callback(_ model: inout Model)


struct CrappyIncrementerDelegate: CrappyDelegate 
    let step: Int

    func callback(_ model: inout Model) 
        model.i = 9999999
        // Just hijacked the models value,
        // and the controller can't do anything about it.
    


class VulnerableController 
    var model = Model()
    var delegate: CrappyDelegate

    init(delegate: CrappyDelegate) 
        self.delegate = delegate
    

    // To be called externally, such as by a button
    func doSomething() 
        // Controller leaks entire model, and has no control over what happens to it
        delegate.callback(&model)
    


let crappyDelegate = CrappyIncrementerDelegate(step: 5)
let vulnerableController = VulnerableController(delegate: crappyDelegate)
print(controller.model.i)
controller.doSomething() // simulate button press
print(controller.model.i) // model hijacked

【讨论】:

【参考方案2】:

如果你想通过引用传递,你通常应该使用class 而不是struct

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html 状态:

您可以使用类和结构来定义自定义数据类型以 用作程序代码的构建块。

但是,结构实例总是按值传递,而类 实例总是通过引用传递。这意味着他们是 适合不同类型的任务。当你考虑数据 项目所需的结构和功能,决定 每个数据结构应该定义为一个类还是一个 结构。

【讨论】:

【参考方案3】:

structs 总是按值传递。使用struct 的全部意义在于让它表现为一个值类型。如果你需要委托(这通常意味着可变状态),你应该使用一个类。

如果您真的需要,您可以使用inout 参数强制传递引用,但一般不建议这样做。您还可以使用box type 来模拟通过引用传递。但是,一般来说,如果你需要引用行为,你应该只使用一个类。

【讨论】:

一切,无论类型如何,在不使用inout时总是按值传递。 嗯,从技术上讲是的,但是因为当你传递一个类引用时,你实际上是按值传递一个引用,它的作用几乎与按引用传递相同。

以上是关于在 Swift 中传递值类型作为引用的主要内容,如果未能解决你的问题,请参考以下文章

swift基本语法——数据类型

Swift 值类型和引用类型

在 Swift 中检查值或引用类型

c# 值传递 引用传递

Swift 值类型 和 引用类型的区别

Swift 值类型 和 引用类型的区别