Swift: Capture inout parameter in closures that escape the called function

后端 未结 2 905
后悔当初
后悔当初 2021-02-10 04:44

I tried to write an \"editor\" class that could retain a reference to a property on a different object for later mutation. I first wrote the editor class to receive a closure f

相关标签:
2条回答
  • 2021-02-10 05:37

    It can be done as follows. )Notice that the closure and the inout param have the same lifespan.)

    /// A class providing access to a resource with an inout parameter in an escaping closure.   
         class ProtectedResource<ValueType> {
                private var protectedResourceArray = [ValueType]()
                private var protectedResourceArrayLock = NSRecursiveLock()
                private let opq = OperationQueue()
    
                func performWithResource(block: @escaping (inout [ValueType]) -> ()) {
                    opq.addOperation { [weak self] in
                        guard let strongSelf = self else {
                            return
                        }
                        strongSelf.protectedResourceArrayLock.lock()
                        block(&strongSelf.protectedResourceArray)
                        strongSelf.protectedResourceArrayLock.unlock()
                    }
                }
            }
    
    /// Some other class using that in out parameter. 
    
            func run() {
    
                func updateArray(array: inout [String]) {
                    print("Performing on \(array)")
                    array.append("test")
                }
    
                protectedResource.performWithResource(block: updateArray)
    
                protectedResource.performWithResource {
                    print("Performing on \($0)")
                }
        }
    
    0 讨论(0)
  • 2021-02-10 05:38

    I don't think this is possible. And it shouldn't be possible, if you think about it, because it would be super unsafe.

    Because closures can outlive the scope they were created in, captured variables must be stored with the block. But in order to be able to assign to the captured variable and share the state of that variable between the (one or more) block(s) that captured it and the original scope, the blocks cannot just capture the value of the variable (which would create independent copies of the variable), but capture a kind of "reference" to a shared copy. This means that assignable variables that are captured by blocks must be stored specially. In Objective-C, this is declared with __block. In Swift, this __block behavior is implicit.

    However, in order for the block to modify an inout variable (potentially at a later time) as it is seen in the function caller's scope, that would mean that the passed variable in the caller's scope would also need to be stored in a way that can outlive the stack frame. But the caller function doesn't know this. All it knows from the type of the called function is that one of its parameters is inout; it doesn't know that the function plans to capture that inout variable in a block. So it doesn't know to prepare this __block storage for this passed variable.

    0 讨论(0)
提交回复
热议问题