Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/lua/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift:闭包是否引用常量或变量?_Swift_Closures - Fatal编程技术网

Swift:闭包是否引用常量或变量?

Swift:闭包是否引用常量或变量?,swift,closures,Swift,Closures,我知道有几个相关的问题,而且我可以在网上找到很多帖子。 然而,我不能理解闭包可以保存引用的事实。对于引用类型,这是非常常见和合理的,但是值类型如何,包括struct和enum? 请参阅此代码 let counter: () -> Int var count = 0 do { counter = { count += 1 return count } } count += 1 // 1 counter() // 2 counter() //

我知道有几个相关的问题,而且我可以在网上找到很多帖子。 然而,我不能理解闭包可以保存引用的事实。对于引用类型,这是非常常见和合理的,但是值类型如何,包括
struct
enum
? 请参阅此代码

let counter: () -> Int
var count = 0
do  {
    counter = {
        count += 1
        return count
    }
}
count += 1 // 1
counter() // 2
counter() // 3
我们可以通过两种方式访问值类型
count
。一种是直接使用
计数
,另一种是通过闭包
计数器
。 然而,如果我们写

let a = 0
let b = a
在内存中,
b
当然有一个与
a
不同的区域,因为它们是值类型。这种行为是值类型的一个显著特征,与引用类型不同。 然后回到闭包主题,闭包引用了值类型的变量或常量

所以,我可以说,在闭包捕获值的情况下,值类型的特性(我们不能对值类型进行任何引用)发生了变化吗? 对我来说,捕获对值类型的引用是非常令人惊讶的,同时我在上面展示的经验表明了这一点


你能解释一下这件事吗?

我认为混淆是因为对值类型和引用类型考虑太多了。这与此无关。让我们将数字设为参考类型:

class RefInt: CustomStringConvertible {
    let value: Int
    init(value: Int) { self.value = value }
    var description: String { return "\(value)" }
}

let counter: () -> RefInt
var count = RefInt(value: 0)
do  {
    counter = {
        count = RefInt(value: count.value + 1)
        return count
    }
}
count = RefInt(value: count.value + 1) // 1
counter() // 2
counter() // 3
这感觉有什么不同吗?我希望不是。这是一样的,只是参考文献。这不是一件有价值/有参考价值的事情

关键是,正如您所注意到的,闭包捕获了变量。不是变量的值或变量指向的引用的值,而是变量本身)。因此,在捕获该变量的所有其他位置(包括调用方)都可以看到闭包内变量的更改。这将在中进行更全面的讨论

如果您感兴趣,请再深入一点(现在我将讨论一些可能超出您现在所关心的技术细节):

闭包实际上有一个对变量的引用,它们所做的更改会立即发生,包括调用
didSet
,等等。这与
inout
参数不同,后者仅在返回时才将值分配给其原始上下文。你可以这样看:

let counter: () -> Int
var count = 0 {
    didSet { print("set count") }
}

do  {
    counter = {
        count += 1
        print("incremented count")
        return count
    }
}

func increaseCount(count: inout Int) {
    count += 1
    print("increased Count")
}

print("1")
count += 1 // 1
print("2")
counter() // 2
print("3")
counter() // 3

increaseCount(count: &count)
这张照片是:

1
set count
2
set count
incremented count
3
set count
incremented count
increased Count
set count

注意“set count”总是在“incremented count”之前,而在“incremented count”之后。这让我们明白闭包实际上引用的是它们捕获的同一个变量(不是值或引用;变量),以及为什么我们称它为闭包的“捕获”,而不是“传递”给函数。(当然,您也可以“传递”到闭包,在这种情况下,闭包的行为与这些参数上的函数完全相同。)

我认为混淆之处在于对值类型和引用类型的考虑太多。这与此无关。让我们将数字设为参考类型:

class RefInt: CustomStringConvertible {
    let value: Int
    init(value: Int) { self.value = value }
    var description: String { return "\(value)" }
}

let counter: () -> RefInt
var count = RefInt(value: 0)
do  {
    counter = {
        count = RefInt(value: count.value + 1)
        return count
    }
}
count = RefInt(value: count.value + 1) // 1
counter() // 2
counter() // 3
这感觉有什么不同吗?我希望不是。这是一样的,只是参考文献。这不是一件有价值/有参考价值的事情

关键是,正如您所注意到的,闭包捕获了变量。不是变量的值或变量指向的引用的值,而是变量本身)。因此,在捕获该变量的所有其他位置(包括调用方)都可以看到闭包内变量的更改。这将在中进行更全面的讨论

如果您感兴趣,请再深入一点(现在我将讨论一些可能超出您现在所关心的技术细节):

闭包实际上有一个对变量的引用,它们所做的更改会立即发生,包括调用
didSet
,等等。这与
inout
参数不同,后者仅在返回时才将值分配给其原始上下文。你可以这样看:

let counter: () -> Int
var count = 0 {
    didSet { print("set count") }
}

do  {
    counter = {
        count += 1
        print("incremented count")
        return count
    }
}

func increaseCount(count: inout Int) {
    count += 1
    print("increased Count")
}

print("1")
count += 1 // 1
print("2")
counter() // 2
print("3")
counter() // 3

increaseCount(count: &count)
这张照片是:

1
set count
2
set count
incremented count
3
set count
incremented count
increased Count
set count

注意“set count”总是在“incremented count”之前,而在“incremented count”之后。这让我们明白闭包实际上引用的是它们捕获的同一个变量(不是值或引用;变量),以及为什么我们称它为闭包的“捕获”,而不是“传递”给函数。(当然,您也可以“传递”到闭包,在这种情况下,闭包的行为与这些参数上的函数完全相同。)

我不确定您希望获得多少技术细节,但&可能有用。基本上,捕获的值被放入堆上的引用计数框中,然后存储在函数值的上下文对象中。当涉及到捕获持有引用类型的变量时,引用本身被放入heap-allocated框中(考虑到引用本身是值类型),因此实际上您拥有的是对引用的引用。我阅读了您的文章,这非常令人兴奋。我理解了内容。然而,什么是引用计数框?我看过你的答案很多次了,但我还没见过这个词。你能解释一下吗?很高兴你发现它有用!一个框只是一个给定值的包装器,因此使用闭包捕获,捕获的变量的值被“装箱”到堆中的一个结构中,该结构具有我的答案中专门的
box
struct的布局。然后(大多数情况下)将来对该变量的任何访问(在定义该变量的范围内以及闭包中)都被编译为访问堆上的装箱值。尽管如此,严格来说,在您的示例中,
count
是一个全局变量,因此具有静态生存期,这意味着闭包在技术上并没有捕获它——它只是正常地访问它(如果您感兴趣,我会稍微介绍一下全局和静态访问器的实现)。在本例中,引用计数只是跟踪堆上内存块的生存期的一种方法,顾名思义,它是通过保持对该内存的引用数的计数来完成的,当引用数==0时将其释放。斯威夫特经常这样做。我不知道有多少