在代码中看到一些意想不到的行为后,我创建了以下游乐场,这让我很惊讶:
import Foundation
let bytes:[UInt8] = [20, 30, 40, 50, 60, 70]
var stream = bytes.generate()
func consumeTwo(var stream:IndexingGenerator<[UInt8]>) {
print(stream.next())
print(stream.next())
}
consumeTwo(stream) // This prints 20 and 30
print(stream.next()) // This prints 20!? I expected 40!
我以为将
stream
参数标记为var
到consumeTwo()
函数时,流状态/位置将在从一个函数移到另一个函数时共享/更新。但事实似乎并非如此。这是否意味着我需要把它变成一个
inout
?带着符号过去?如果是这样,什么时候使用var
?一般来说…在字节数组上创建流的正确/惯用方法是什么?字节数组可以从一个函数传递到另一个函数(例如解码器),并在流传递时保留流的位置。
最佳答案
+1代表题目中的古英语。:)
在函数签名中使用var
时,将创建该值的本地副本。就像你这样做一样:
func consumeTwo(stream: IndexingGenerator<[UInt8]>) {
var localStream = stream
print(localStream.next())
print(localStream.next())
}
当参数是引用类型(即类)时,重复的“值”是对同一对象的重复引用。但是从
Array.generate()
得到的是一个值类型,因此本地副本是一个具有不同状态的独立迭代器。这是否意味着我需要把它变成一个
inout
?带着符号过去?是-对于您的简单示例,
inout
(并通过&
)是一种简单而惯用的方法:func consumeTwo(inout stream:IndexingGenerator<[UInt8]>) {
print(stream.next())
print(stream.next())
}
consumeTwo(&stream) // This prints 20 and 30
print(stream.next()) // This prints 40
记住:当您想修改一个值类型
in
side a函数,然后看到这些修改out
side the函数时,请使用inout
。&
也会随之出现,这样就可以清楚地看到函数内部和调用位置都发生了这种行为。如果是这样,什么时候使用
var
?仅当您希望复制函数调用的本地副本时,才对参数使用
var
。不可否认,这方面的用例很少。这里有一个人为的(完全没有必要的)方法:func bananify(var strings: [String]) {
for i in 1.stride(to: strings.count, by: 2) {
strings[i] = "banana"
}
print(strings.joinWithSeparator(" "))
}
let words = ["foo", "bar", "bas", "zap", "asdf"]
bananify(words) // "foo banana bas banana asdf\n"
如果你感到困惑,你不是唯一的一个。因此,取消对参数使用
var
的能力是一个planned change for Swift 3。一般来说…在字节数组上创建流的正确/惯用方法是什么?字节数组可以从一个函数传递到另一个函数(例如解码器),并在流传递时保留流的位置。
As user3441734 notes,您确实可以创建并使用引用类型迭代器。或者可以编写一个引用类型来保存和管理迭代器。对于在程序的几个子系统之间共享流的假设情况,这可能是一个好方法-representing a shared resource is one of the canonical cases for using reference types。
关于swift - 如何在Swift中的函数之间共享流位置?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34140550/