struct Foo {
var i = 0 { didSet { println("Current i: \(i)") } }
func delayedPrint() {
dispatch_async(dispatch_get_main_queue(), { _ in
println("Closure i: \(self.i)")
})
}
mutating func foo() {
delayedPrint()
i++
}
}现在的输出
var a = Foo()
a.foo()是
Current i: 1
Closure i: 0 // I want current value here.我想知道什么是最好的方法,以避免捕获一个象牙副本。
编辑1
是的,搬去上课是我想到的第一件也是唯一一件事,但是.这一次愚弄我以为可以用结构来实现.为什么会起作用?
mutating func foo() {
delayedPrint()
dispatch_async(dispatch_get_main_queue(), { _ in
println("From foo: \(self.i)")
})
delayedPrint()
i++
}输出:
Current i: 1
Closure i: 0
From foo: 1
Closure i: 0发布于 2015-05-05 18:41:02
我想知道什么是最好的方法,以避免捕获一个象牙副本。
这是一种误解。你不能以这种方式“捕获一个象牙”。你要捕捉的是self!这就是为什么斯威夫特强迫你说self,这样你才能理解这个事实。因此,self是一种不同的东西。这就是为什么self是一个结构还是一个类很重要的原因。类实例是可变的;struct实例不是,因此在捕获时获取一个副本,并且该副本独立保存。
但是,您可以捕获一个简单的Int (即不是ivar),当您这样做时,您就得到了预期的结果:
var i = 0
struct Foo {
func delayedPrint() {
dispatch_async(dispatch_get_main_queue(), {
println(i) // 1
})
}
func foo() {
delayedPrint()
i++
}
}现在让我们来谈谈你提出的第二个谜题。这里有一个重写,以澄清谜题是什么:
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
struct Foo {
var i = 0
mutating func foo() {
delay(0.5) {
println("From foo: \(self.i)") // 1
}
bar(2)
i++
}
func bar(d:Double) {
delay(d) {
println("from bar: \(self.i)") // 0
}
}
}我会像这样测试它
var a = Foo()
a.foo()
a.bar(1)控制台显示:
From foo: 1 [after half a second]
from bar: 1 [after 1 second]
from bar: 0 [after 2 seconds]那么,如何在1秒后的第二次调用bar,同时显示更早的self.i值呢?为什么foo的行为会有所不同?
答案与以下事实有关:所有事情都发生在函数内部--包括匿名函数的定义。代码必须在某个时候运行。在此之前,匿名函数尚未定义。
a.bar(1)。这将导致bar运行并定义将捕获self的匿名函数。但是在我们调用foo和增量i之后就会发生这种情况。因此,此时捕获的self有一个递增的i。foo调用bar时会发生什么。它在增量i之前就这样做了。因此,现在bar运行并定义了匿名函数,并捕获了i仍然设置为0的self。这个结果在两秒钟后到达控制台是不相关的;重要的是捕获发生的时间。foo中的匿名函数这个令人惊讶的例子。显然,i++在foo中的存在决定了一切的不同。为什么?当foo运行时,它定义了一个捕捉self的匿名函数。但是,这个self也是在foo本身中捕获的,目的是为了表示i++ --实际上是self.i++。因此,这个匿名函数也可以看到i++在i++上执行的更改,因为它们查看的是同一个self。
换句话说,我建议您已经碰到了匿名函数中定义的匿名函数的神秘边缘情况,该函数本身会变异self。(我不知道我是否认为这是一个bug;我将把它提交给dev论坛,看看他们怎么想。)发布于 2015-05-05 17:59:49
我认为这里必须使用类而不是结构,因为结构是通过复制传递的,而类是通过引用传递的。
发布于 2015-05-05 18:22:16
为了补充@nikolayn的完美答案,这里有一个可以在控制台中运行的示例,它演示了如何使用类而不是结构(并且也没有数据竞争)来实现这一点:(变量是显式定义的,这样您就可以很容易地调试)
import Foundation
import Cocoa
let queue = dispatch_queue_create("sync_queue", nil)!
class Foo {
var i = 0 { didSet { println("Current i: \(i)") } }
let sem = dispatch_semaphore_create(0);
func delayedPrint() {
let i_copy = i
let t = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
dispatch_after(t, queue) { _ in
let _i = self.i
let _i_copy = i_copy
println("Closure i: \(_i)")
println("Closure i_copy: \(_i_copy)")
dispatch_semaphore_signal(self.sem)
}
}
func foo() {
delayedPrint()
dispatch_async(queue) {
self.i++
}
}
func wait() {
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)
}
}
var a = Foo()
a.foo()
a.wait()https://stackoverflow.com/questions/30059508
复制相似问题