这是我用Swift进行的一个小实验:
func store<T>(var x: T) -> (getter: (Void -> T), setter: (T -> Void)) {
return ({ x }, { x = $0 })
}
x
是一个值类型。
我的问题是:
x
究竟存储在哪里(栈/堆)?- 以这种方式存储
x
的缺陷是什么? - 这样做是安全的吗?
x
何时会被销毁(如果有)?
这是我用Swift进行的一个小实验:
func store<T>(var x: T) -> (getter: (Void -> T), setter: (T -> Void)) {
return ({ x }, { x = $0 })
}
x
是一个值类型。
我的问题是:
x
究竟存储在哪里(栈/堆)?x
的缺陷是什么?x
何时会被销毁(如果有)?参数通过值传递给函数和方法——这意味着参数的副本会被创建并在函数体中使用。
函数和方法接收到的参数是不可变的,这意味着它们的值无法更改。然而,var
修饰符可以使参数变为可变——需要注意的重要一点是:参数的副本是可变的,传递给函数的参数与函数体接收到的参数没有关系,除了最初的副本。也就是说,通过var
修饰符使参数可变会使其发生变化,但其生命周期仅限于函数体,不会影响传递给函数的原始参数。
还有另一种选择,即inout
修饰符,它的工作方式类似于var
,但当函数返回时,值将被复制回传递的变量中。
值得一提的是,到目前为止,我隐含地只考虑了值类型。如果将引用类型实例(类或闭包)作为var
参数传递给函数,则通过该参数进行的任何更改实际上都是针对传递给函数的实例进行的(这是值类型和引用类型之间最显着的区别)。由x
变量指向的实例与传递给函数的参数具有相同的生命周期。
所有这些说法,在你的情况中,它的工作方式略有不同。你正在返回一个闭包(好吧,它们是2个,但这并不改变结论),该闭包捕获x
,从而导致x
在变量分配闭包的范围内保持活动:
let x = 5
let (getter, setter) = store(x)
在上述代码中,当getter
和setter
被释放时,x
(作为在store
函数中定义的变量)也将停止存在。
回答你的问题:
x
是在调用store
函数时创建的变量。由于您明确提到值类型,x
应该分配在堆栈上(与应该用于引用类型的堆相对)x
也会被释放
T
是值类型,那么变量将分配在堆栈上,但是闭包(作为引用类型)将分配在堆上。因此,数据存储在堆栈上,并由分配在堆上的两个实例保持活动状态。 - Antonioinout
并不是通过引用传递:它会先复制一份,然后再复制回去。(至少在与值一起使用时是这样的) - oisdk&
自动转换为“引用” - 太多年花在 C++ 上了 :) - Antonio