在方法loadHappinessV1中:
>如果未指定self,编译器会引发错误:error:在闭包中对属性’callbackQueue’的引用需要显式的’self’.使捕获语义显式化
>为了防止编译器错误,我指定对self的弱引用.
在方法loadHappinessV2中:
>我决定引入两个嵌套函数并简化 *** 作的“主体”.
>编译器不会引发有关捕获语义的错误.
为什么在方法loadHappinessV2编译器中不会引发有关捕获语义的错误?是否未捕获嵌套函数(以及变量callbackQueue)?
谢谢!
import PlaygroundSupportimport CocoaPlaygroundPage.current.needsIndefiniteExecution = truestruct 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) } }}// Usagelet happinessNetV1 = Happiness.Net(callbackQueue: dispatchQueue.main)happinessNetV1.loadHappinessV1 { switch解决方法 我找到了一些解释如何使用嵌套函数捕获语义.资料来源: 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 }}{ 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 { switchsil_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,%1bb0(%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}{ case .success: print("Happiness V2 delivered .)") case .failure: print("Happiness V2 not available at the moment .(") }}
考虑以下示例:
sil [transparent] [fragile] @Swift.Int.init (_builtinintegerliteral : Builtin.Int2048) -> Swift.Int : $@convention(method) (Builtin.Int2048,@thin Int.Type) -> Intsil_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}
编译器提醒我们保持函数closureA的所有权.但是没有说明在功能函数A中捕获自我的任何信息.
让我们看看Swift中间语言(SIL):
xcrun swiftc -emit-silgen Test.swift | xcrun swift-demangle> Test.silgen
import PlaygroundSupportimport CocoaPlaygroundPage.current.needsIndefiniteExecution = truestruct 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) } }}// Usagelet happinessNetV1 = Happiness.Net(callbackQueue: dispatchQueue.main)happinessNetV1.loadHappinessV1 { switch { 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 { case .success: print("Happiness V2 delivered .)") case .failure: print("Happiness V2 not available at the moment .(") }}
行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.
因此,如果嵌套函数访问self中定义的某些属性,那么嵌套函数将保留对self的强引用.编译器不会通知它(Swift 3.0.1).
为了避免这种行为,我们只需要使用闭包而不是嵌套函数.然后编译器将通知自我使用情况.
原始示例可以重新表示如下:
总结以上是内存溢出为你收集整理的Swift:从闭包调用嵌套函数时捕获语义.为什么编译器不会引发错误?全部内容,希望文章能够帮你解决Swift:从闭包调用嵌套函数时捕获语义.为什么编译器不会引发错误?所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)