在 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】:struct
s 总是按值传递。使用struct
的全部意义在于让它表现为一个值类型。如果你需要委托(这通常意味着可变状态),你应该使用一个类。
如果您真的需要,您可以使用inout
参数强制传递引用,但一般不建议这样做。您还可以使用box type 来模拟通过引用传递。但是,一般来说,如果你需要引用行为,你应该只使用一个类。
【讨论】:
一切,无论类型如何,在不使用inout
时总是按值传递。
嗯,从技术上讲是的,但是因为当你传递一个类引用时,你实际上是按值传递一个引用,它的作用几乎与按引用传递相同。以上是关于在 Swift 中传递值类型作为引用的主要内容,如果未能解决你的问题,请参考以下文章