需要你的帮助来理解当从闭包中调用嵌套函数时Swift捕获语义是如何工作的.所以,我有两种方法loadHappinessV1和loadHappinessV2.
在方法中loadHappinessV1:
self未指定,编译器会引发错误:error:在闭包中引用属性'callbackQueue'需要显式的'self'.使捕获语义显式化self.在方法中loadHappinessV2:
为什么在方法loadHappinessV2编译器中不会引发有关捕获语义的错误?是否callbackQueue未捕获嵌套函数(与变量一起)?
谢谢!
import PlaygroundSupport
import Cocoa
PlaygroundPage.current.needsIndefiniteExecution = true
struct Happiness {
final class Net {
enum LoadResult {
case success
case failure
}
private var callbackQueue: DispatchQueue
private lazy var operationQueue = OperationQueue()
init(callbackQueue: DispatchQueue) {
self.callbackQueue = callbackQueue
}
func loadHappinessV1(completion: (LoadResult) -> Void) {
operationQueue.cancelAllOperations()
let hapynessOp = BlockOperation { [weak self] in
let hapynessGeneratorValue = arc4random_uniform(10)
if hapynessGeneratorValue % 2 == 0 {
// callbackQueue.async { completion(.success) } // Compile error
self?.callbackQueue.async { completion(.success) }
} else {
// callbackQueue.async { completion(.failure) } // Compile error
self?.callbackQueue.async { completion(.failure) }
}
}
operationQueue.addOperation(hapynessOp)
}
func loadHappinessV2(completion: (LoadResult) -> Void) {
operationQueue.cancelAllOperations()
func completeWithFailure() {
callbackQueue.async { completion(.failure) }
}
func completeWithSuccess() {
callbackQueue.async { completion(.success) }
}
let hapynessOp = BlockOperation {
let hapynessGeneratorValue = arc4random_uniform(10)
if hapynessGeneratorValue % 2 == 0 {
completeWithSuccess()
} else {
completeWithFailure()
}
}
operationQueue.addOperation(hapynessOp)
}
}
}
// Usage
let happinessNetV1 = Happiness.Net(callbackQueue: DispatchQueue.main)
happinessNetV1.loadHappinessV1 {
switch $0 {
case .success: print("Happiness V1 delivered .)")
case .failure: print("Happiness V1 not available at the moment .(")
}
}
let happinessNetV2 = Happiness.Net(callbackQueue: DispatchQueue.main)
happinessNetV2.loadHappinessV2 {
switch $0 {
case .success: print("Happiness V2 delivered .)")
case .failure: print("Happiness V2 not available at the moment .(")
}
}
Run Code Online (Sandbox Code Playgroud)
我找到了一些解释如何使用嵌套函数捕获语义。来源:嵌套函数和引用捕获。
考虑以下示例:
class Test {
var bar: Int = 0
func functionA() -> (() -> ()) {
func nestedA() {
bar += 1
}
return nestedA
}
func closureA() -> (() -> ()) {
let nestedClosureA = { [unowned self] () -> () in
self.bar += 1
}
return nestedClosureA
}
}
Run Code Online (Sandbox Code Playgroud)
Compiler 提醒我们在 function 中保持所有权closureA。但没有说明self在函数中捕获的任何信息functionA。
让我们看看 Swift 中级语言 ( SIL ):
xcrun swiftc -emit-silgen Test.swift | xcrun swift-demangle > Test.silgen
sil_scope 2 { loc "Test.swift":5:10 parent @Test.Test.functionA () -> () -> () : $@convention(method) (@guaranteed Test) -> @owned @callee_owned () -> () }
sil_scope 3 { loc "Test.swift":10:5 parent 2 }
// Test.functionA() -> () -> ()
sil hidden @Test.Test.functionA () -> () -> () : $@convention(method) (@guaranteed Test) -> @owned @callee_owned () -> () {
// %0 // users: %4, %3, %1
bb0(%0 : $Test):
debug_value %0 : $Test, let, name "self", argno 1, loc "Test.swift":5:10, scope 2 // id: %1
// function_ref Test.(functionA() -> () -> ()).(nestedA #1)() -> ()
%2 = function_ref @Test.Test.(functionA () -> () -> ()).(nestedA #1) () -> () : $@convention(thin) (@owned Test) -> (), loc "Test.swift":9:16, scope 3 // user: %4
strong_retain %0 : $Test, loc "Test.swift":9:16, scope 3 // id: %3
%4 = partial_apply %2(%0) : $@convention(thin) (@owned Test) -> (), loc "Test.swift":9:16, scope 3 // user: %5
return %4 : $@callee_owned () -> (), loc "Test.swift":9:9, scope 3 // id: %5
}
Run Code Online (Sandbox Code Playgroud)
该行strong_retain %0 : $Test, loc "Test.swift":9:16, scope 3 // id: %3告诉我们编译器对$Test(定义为self)进行强引用,该引用存在于作用域3(即functionA)中,并且在离开作用域时不会释放3。
第二个函数closureA处理对 的可选引用self。它在代码中表示为%2 = alloc_box $@sil_weak Optional<Test>, var, name "self", loc "Test.swift":13:38, scope 8 // users: %13, %11, %9, %3。
sil [transparent] [fragile] @Swift.Int.init (_builtinIntegerLiteral : Builtin.Int2048) -> Swift.Int : $@convention(method) (Builtin.Int2048, @thin Int.Type) -> Int
sil_scope 6 { loc "Test.swift":12:10 parent @Test.Test.closureA () -> () -> () : $@convention(method) (@guaranteed Test) -> @owned @callee_owned () -> () }
sil_scope 7 { loc "Test.swift":17:5 parent 6 }
sil_scope 8 { loc "Test.swift":15:9 parent 7 }
// Test.closureA() -> () -> ()
sil hidden @Test.Test.closureA () -> () -> () : $@convention(method) (@guaranteed Test) -> @owned @callee_owned () -> () {
// %0 // users: %5, %4, %1
bb0(%0 : $Test):
debug_value %0 : $Test, let, name "self", argno 1, loc "Test.swift":12:10, scope 6 // id: %1
%2 = alloc_box $@sil_weak Optional<Test>, var, name "self", loc "Test.swift":13:38, scope 8 // users: %13, %11, %9, %3
%3 = project_box %2 : $@box @sil_weak Optional<Test>, loc "Test.swift":13:38, scope 8 // users: %10, %6
strong_retain %0 : $Test, loc "Test.swift":13:38, scope 8 // id: %4
%5 = enum $Optional<Test>, #Optional.some!enumelt.1, %0 : $Test, loc "Test.swift":13:38, scope 8 // users: %7, %6
store_weak %5 to [initialization] %3 : $*@sil_weak Optional<Test>, loc "Test.swift":13:38, scope 8 // id: %6
release_value %5 : $Optional<Test>, loc "Test.swift":13:38, scope 8 // id: %7
// function_ref Test.(closureA() -> () -> ()).(closure #1)
%8 = function_ref @Test.Test.(closureA () -> () -> ()).(closure #1) : $@convention(thin) (@owned @box @sil_weak Optional<Test>) -> (), loc "Test.swift":13:30, scope 8 // user: %11
strong_retain %2 : $@box @sil_weak Optional<Test>, loc "Test.swift":13:30, scope 8 // id: %9
mark_function_escape %3 : $*@sil_weak Optional<Test>, loc "Test.swift":13:30, scope 8 // id: %10
%11 = partial_apply %8(%2) : $@convention(thin) (@owned @box @sil_weak Optional<Test>) -> (), loc "Test.swift":13:30, scope 8 // users: %14, %12
debug_value %11 : $@callee_owned () -> (), let, name "nestedClosureA", loc "Test.swift":13:13, scope 7 // id: %12
strong_release %2 : $@box @sil_weak Optional<Test>, loc "Test.swift":15:9, scope 7 // id: %13
return %11 : $@callee_owned () -> (), loc "Test.swift":16:9, scope 7 // id: %14
}
Run Code Online (Sandbox Code Playgroud)
因此,如果嵌套函数访问 中定义的某些属性self,则嵌套函数会保持对 的强引用self。编译器不会通知它(Swift 3.0.1)。
为了避免这种行为,我们只需要使用闭包而不是嵌套函数。然后编译器会通知self使用情况。
原始示例可以如下重新加载:
import PlaygroundSupport
import Cocoa
PlaygroundPage.current.needsIndefiniteExecution = true
struct Happiness {
final class Net {
enum LoadResult {
case success
case failure
}
private var callbackQueue: DispatchQueue
private lazy var operationQueue = OperationQueue()
init(callbackQueue: DispatchQueue) {
self.callbackQueue = callbackQueue
}
func loadHappinessV1(completion: @escaping (LoadResult) -> Void) {
operationQueue.cancelAllOperations()
let hapynessOp = BlockOperation { [weak self] in
let hapynessGeneratorValue = arc4random_uniform(10)
if hapynessGeneratorValue % 2 == 0 {
// callbackQueue.async { completion(.success) } // Compile error
self?.callbackQueue.async { completion(.success) }
} else {
// callbackQueue.async { completion(.failure) } // Compile error
self?.callbackQueue.async { completion(.failure) }
}
}
operationQueue.addOperation(hapynessOp)
}
func loadHappinessV2(completion: @escaping (LoadResult) -> Void) {
operationQueue.cancelAllOperations()
// Closure used instead of nested function.
let completeWithFailure = { [weak self] in
self?.callbackQueue.async { completion(.failure) }
}
// Closure used instead of nested function.
let completeWithSuccess = { [weak self] in
self?.callbackQueue.async { completion(.success) }
}
let hapynessOp = BlockOperation {
let hapynessGeneratorValue = arc4random_uniform(10)
if hapynessGeneratorValue % 2 == 0 {
completeWithSuccess()
} else {
completeWithFailure()
}
}
operationQueue.addOperation(hapynessOp)
}
}
}
// Usage
let happinessNetV1 = Happiness.Net(callbackQueue: DispatchQueue.main)
happinessNetV1.loadHappinessV1 {
switch $0 {
case .success: print("Happiness V1 delivered .)")
case .failure: print("Happiness V1 not available at the moment .(")
}
}
let happinessNetV2 = Happiness.Net(callbackQueue: DispatchQueue.main)
happinessNetV2.loadHappinessV2 {
switch $0 {
case .success: print("Happiness V2 delivered .)")
case .failure: print("Happiness V2 not available at the moment .(")
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
786 次 |
| 最近记录: |