2016-06-15 11 views
2

クロージャーを別のクロージャーに持っていれば、クロージャーを保持しないためにアウタークロージャーに一度無人/弱を使用すれば十分ですか?閉鎖のクロージャーで所有されていない自己

例:?あなたが弱い使用し、その自己.doStuff(もしながらself.doStuff()を持つがnil場合に例外をスローするので

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

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

何を達成したいですか? – kandelvijaya

答えて

0

はい、しかし、私は所有されていない上、弱使用します)「は勝ちました例外がスローされ、実行されません。

次のコードを使用して、運動場でこれをテストすることができます。

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?() 

とと比較:のみ、外閉鎖のキャプチャリストに弱いか所有していない自己を宣言

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?() 
1

があります外側のクロージャ内で自分自身への強い参照を作成しないと、保持サイクルを回避するのに十分です(例えば:guard let strongSelf = self else {return})。

クロージャ内で強い参照を作成する場合は、内側のクロージャにキャプチャリストを追加して、自分の弱い参照を確実にキャプチャするようにする必要があります。

ここではいくつかの例があります:

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 
+0

copiedSelfWithoutInnerCaptureListについて教えてください。私はこの1つだけをチェックして、 "デニット"を得たので、保持していませんでした。 – eilas

+0

各 'outerClosure'のバリエーションの上のコメントで述べたように、テストは 'deinit'が表示されるかどうかではなく、 '#2 calls'が表示されるかどうかではありません。だから、copiedSelfWithoutInnerCaptureListはこう言っています。「この閉鎖は自己を保持しているので、doSomething#2の呼び出しを以下に見るべきです。これは、SomeCloneオブジェクトが保持されている場合にのみ、innerClosure(#2呼び出しを作成する)が実行されるためです。 – jblack

関連する問題