在代码中看到一些意想不到的行为后,我创建了以下游乐场,这让我很惊讶:

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参数标记为varconsumeTwo()函数时,流状态/位置将在从一个函数移到另一个函数时共享/更新。但事实似乎并非如此。
这是否意味着我需要把它变成一个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

记住:当您想修改一个值类型inside a函数,然后看到这些修改outside 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/

10-11 22:25
查看更多