在看到代码中的一些意外行为后,我对创建的以下游乐场感到惊讶:
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
,然后再查看函数边的out
修改时,请使用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 asdfn"
如果你觉得这令人困惑,你不是唯一一个。因此,取消使用var
作为参数的功能是Swift 3的一项计划更改。
更普遍地说。。。在字节数组上创建流的正确/惯用方法是什么?字节数组可以从一个函数传递到另一个函数(例如解码器),并在传递时保留流的位置?
正如user3441734所指出的,您确实可以创建并使用引用类型迭代器。或者,您可以编写一个引用类型来保存和管理迭代器。对于在程序的几个子系统之间共享流的假设情况,这可能是一种很好的方法——表示共享资源是使用引用类型的规范情况之一。
你写道:"这让我希望生成器是对象而不是结构。"
将某些生成器定义为引用类型是没有问题的。。。
class G: AnyGenerator<Int> {
var i = 0
override func next() -> Int? {
return i++
}
}
let g = G()
func foo(gen: G)->Void {
print(gen.next())
print(gen.next())
}
foo(g)
print(g.next())
/*
Optional(0)
Optional(1)
Optional(2)
*/