>如果未指定self,编译器会引发错误:error:在闭包中对属性’callbackQueue’的引用需要显式的’self’.使捕获语义显式化
>为了防止编译器错误,我指定对self的弱引用.
>我决定引入两个嵌套函数并简化操作的“主体”.
>编译器不会引发有关捕获语义的错误.
为什么在方法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 .(") } }
解决方法
考虑以下示例:
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):
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,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 .(") } }
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。