Swift:从闭包调用嵌套函数时捕获语义.为什么编译器不会引发错误
需要你的帮助来理解当从闭包中调用嵌套函数时
Swift捕获语义是如何工作的.所以,我有两个方法loadHappinessV1和loadHappinessV2.
在方法loadHappinessV1中: >如果未指定self,编译器会引发错误:error:在闭包中对属性’callbackQueue’的引用需要显式的’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 .(") } } 解决方法
我找到了一些解释如何使用嵌套函数捕获语义.资料来源:
Nested functions and reference capturing.
考虑以下示例: 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 } } 编译器提醒我们保持函数closureA的所有权.但是没有说明在功能函数A中捕获自我的任何信息. 让我们看看Swift中间语言(SIL): 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,scope 3 // id: %3 %4 = partial_apply %2(%0) : $@convention(thin) (@owned Test) -> (),scope 3 // user: %5 return %4 : $@callee_owned () -> (),loc "Test.swift":9:9,scope 3 // id: %5 } 行strong_retain%0:$Test,loc“Test.swift”:9:16,范围3 // id:%3告诉我们编译器为$Test(定义为self)做强引用,此引用存在于范围3(即功能A)并且在离开范围3时未释放. 第二个函数closureA处理self的可选引用.它在代码中表示为%2 = alloc_box $@ sil_weak可选< Test>,var,名称“self”,loc“Test.swift”:13:38,范围8 //用户:?,?,%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,loc "Test.swift":12:10,scope 6 // id: %1 %2 = alloc_box $@sil_weak Optional<Test>,loc "Test.swift":13:38,scope 8 // users: %13,%11,%9,%3 %3 = project_box %2 : $@box @sil_weak Optional<Test>,scope 8 // users: %10,%6 strong_retain %0 : $Test,scope 8 // id: %4 %5 = enum $Optional<Test>,#Optional.some!enumelt.1,%0 : $Test,scope 8 // users: %7,%6 store_weak %5 to [initialization] %3 : $*@sil_weak Optional<Test>,scope 8 // id: %6 release_value %5 : $Optional<Test>,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>,scope 8 // id: %9 mark_function_escape %3 : $*@sil_weak Optional<Test>,scope 8 // id: %10 %11 = partial_apply %8(%2) : $@convention(thin) (@owned @box @sil_weak Optional<Test>) -> (),scope 8 // users: %14,%12 debug_value %11 : $@callee_owned () -> (),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 } 因此,如果嵌套函数访问self中定义的某些属性,那么嵌套函数将保留对self的强引用.编译器不会通知它(Swift 3.0.1). 为了避免这种行为,我们只需要使用闭包而不是嵌套函数.然后编译器将通知自我使用情况. 原始示例可以重新表示如下: 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 .(") } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |