简体   繁体   English

关闭时封闭的无主自我

[英]Unowned self in a closure in a closure

If I have a closure in another closure is it enough to use unowned/weak once in the outer closure to avoid retain cycles? 如果我在另一个闭包中有一个闭包,那么在外闭合中使用无主/弱一次就足以避免保留周期吗?

Example: 例:

foo.aClosure({[unowned self] (allowed: Bool) in
            if allowed {
                self.doStuff()

                self.something.anotherClosure({ (s:String) -> (Void) in
                    self.doSomethingElse(s)
                })   
            }
        })

Only declaring weak or unowned self in the capture list of the outer closure is enough to avoid retain cycles if you don't create a strong reference to self within the outer closure (eg by doing: guard let strongSelf = self else { return }). 如果你没有在外部闭包中创建对self的强引用(例如,通过执行:guard let strongSelf = self else {return}),则仅在外部闭包的捕获列表中声明弱或无主自我就足以避免保留周期。

If you do create a strong reference within the closure, you must add a capture list to the inner closure to ensure that it captures your strong reference to self weakly. 如果确实在闭包中创建了一个强引用,则必须将一个捕获列表添加到内部闭包中,以确保它捕获您对弱自身的强引用。

Here are some examples: 这里有些例子:

import Foundation
import PlaygroundSupport

class SomeObject {
    typealias OptionalOuterClosure = ((Int) -> Void)?
    typealias InnerClosure = () -> Void

    var outerClosure: OptionalOuterClosure

    func setup() {
        // Here are several examples of the outer closure that you can easily switch out below
        // All of these outer closures contain inner closures that need references to self

        // optionalChecks
        //  - has a capture list in the outer closure
        //  - uses the safe navigation operator (?) to ensure that self isn't nil
        // this closure does NOT retain self, so you should not see the #2 calls below
        let optionalChecks: OptionalOuterClosure = { [weak self] callNumber in
            print("outerClosure \(callNumber)")

            self?.delayCaller { [weak self] in
                print("innerClosure \(callNumber)")
                self?.doSomething(callNumber: callNumber)
            }
        }

        // copiedSelfWithInnerCaptureList
        //  - has a capture list in the outer closure
        //  - creates a copy of self in the outer closure called strongSelf to ensure that self isn't nil
        //  - has a capture list in the inner closure
        //  - uses the safe navigation operator (?) to ensure strongSelf isn't nil
        // this closure does NOT retain self, so you should not see the #2 calls below
        let copiedSelfWithInnerCaptureList: OptionalOuterClosure = { [weak self] callNumber in
            guard let strongSelf = self else { return }
            print("outerClosure \(callNumber)")

            strongSelf.delayCaller { [weak strongSelf] in
                print("innerClosure \(callNumber)")
                strongSelf?.doSomething(callNumber: callNumber)
            }
        }

        // copiedSelfWithoutInnerCaptureList
        //  - has a capture list in the outer closure
        //  - creates a copy of self in the outer closure called strongSelf to ensure that self isn't nil
        //  - does NOT have a capture list in the inner closure and does NOT use safe navigation operator
        // this closure DOES retain self, so you should see the doSomething #2 call below
        let copiedSelfWithoutInnerCaptureList: OptionalOuterClosure = { [weak self] callNumber in
            guard let strongSelf = self else { return }
            print("outerClosure \(callNumber)")

            strongSelf.delayCaller {
                print("innerClosure \(callNumber)")
                strongSelf.doSomething(callNumber: callNumber)
            }
        }

        // retainingOuterClosure
        //  - does NOT have any capture lists
        // this closure DOES retain self, so you should see the doSomething #2 call below
        let retainingOuterClosure: OptionalOuterClosure = { callNumber in
            print("outerClosure \(callNumber)")

            self.delayCaller {
                print("innerClosure \(callNumber)")
                self.doSomething(callNumber: callNumber)
            }
        }

        // Modify which outerClosure you would like to test here
        outerClosure = copiedSelfWithInnerCaptureList
    }

    func doSomething(callNumber: Int) {
        print("doSomething \(callNumber)")
    }

    func delayCaller(closure: @escaping InnerClosure) {
        delay(seconds: 1, closure: closure)
    }

    deinit {
        print("deinit")
    }
}

// Handy delay method copied from: http://alisoftware.github.io/swift/closures/2016/07/25/closure-capture-1/
func delay(seconds: Int, closure: @escaping () -> Void) {
    let time = DispatchTime.now() + .seconds(seconds)
    DispatchQueue.main.asyncAfter(deadline: time) {
        print("🕑")
        closure()
    }
}

var someObject: SomeObject? = SomeObject()
someObject?.setup()

// Keep a reference to the outer closure so we can later test if it retained someObject
let copiedOuterClosure = someObject!.outerClosure!

// Call the outer closure once just to make sure it works
copiedOuterClosure(1)

// Wait a second before we destroy someObject to give the first call a chance to work
delay(seconds: 1) {
    // Run the outerClosure again to check if we retained someObject
    copiedOuterClosure(2)

    // Get rid of our reference to someObject before the inner closure runs
    print("de-referencing someObject")
    someObject = nil
}

// Keep the main run loop going so our async task can complete (need this due to how playgrounds work)
PlaygroundPage.current.needsIndefiniteExecution = true

Yes, however I would use weak over unowned because self.doStuff() with throw an exception if nil while if you use weak and its self?.doStuff() there won't be an exception thrown and it just won't execute. 是的,但是我会使用弱的无主,因为self.doStuff()如果你使用弱和它自己的话会抛出异常吗?.doStuff()不会抛出异常而它就不会执行。

You can test this in the playground with the following code: 您可以在操场上使用以下代码进行测试:

typealias Closure = () -> Void

class ClosureObject {
    var closure:Closure?
    func saveClosure(closure:Closure?) {
        self.closure = closure
    }
}

let mainClosureObject = ClosureObject()

class TestObject {
    let closureObject = ClosureObject()
    func log() {
        print("logged")
    }
    func run() {
        mainClosureObject.saveClosure() {[weak self] in
            self?.closureObject.saveClosure() {
                self?.log()
            }
        }
    }
}

var testObject:TestObject? = TestObject()
let closureObject = testObject?.closureObject
testObject?.run()
mainClosureObject.closure?()
closureObject?.closure?()
testObject = nil
closureObject?.closure?()
mainClosureObject.closure?()
closureObject?.closure?()

and compare it with: 并将其与:

typealias Closure = () -> Void

class ClosureObject {
    var closure:Closure?
    func saveClosure(closure:Closure?) {
        self.closure = closure
    }
}

let mainClosureObject = ClosureObject()

class TestObject {
    let closureObject = ClosureObject()
    func log() {
        print("logged")
    }
    func run() {
        mainClosureObject.saveClosure() {
            self.closureObject.saveClosure() {
                self.log()
            }
        }
    }
}

var testObject:TestObject? = TestObject()
let closureObject = testObject?.closureObject
testObject?.run()
mainClosureObject.closure?()
closureObject?.closure?()
testObject = nil
closureObject?.closure?()
mainClosureObject.closure?()
closureObject?.closure?()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM