web-dev-qa-db-ja.com

UIView.animateWithDuration(...)のクロージャーで[unowned self]を使用する必要がありますか?

    UIView.animateWithDuration(1,
        animations: { [unowned self] in
            self.box.center = self.boxTopRightPosition
        },
        completion: { [unowned self] completed in
            self.box.hidden = true
    })

メモリリークを回避する必要がありますか?

38
WildCat

いいえ、この場合は必要ありません。 animationscompletionselfによって保持されないため、強力な保持サイクルのリスクはありません。

53
Kirsteins

まあ、「必要」は「推奨」と同じではありません。質問が必要である場合、@ Kirsteinsの応答は問題ありませんが、何らかの作業の後にビューコントローラで何かをアニメーション化したいが、ビューコントローラが解放された(それがビュー階層にもうないため)と想像してください。またはその他の理由)。この場合、[weak self]を使用しない場合、ビューコントローラはアニメーションブロックに保持しているため、アニメーションが完了するまで解放されませんが、何かをアニメーション化するまで保持しておくことは理にかなっています。もう見えないのはどれ?

したがって、簡単に言えば、UIKitをアニメーション化するときにneedを使用してselfへのweak参照を使用しますが、ビューが解放されている場合、ビューを保持しておく必要はありません。ビューのないアニメーションは意味がないため、weakを使用することをお勧めします。

31
Pablo A.

いいえ、必要ありません。 Kirsteins が言うように:

いいえ、この場合は必要ありません。アニメーションと完了は自己保持されないため、強い保持サイクルのリスクはありません。

しかし lhmgrassi は言う:

割り当てが解除されるとすぐに、非イニシャライザが呼び出され、完了は実行されません。

これは本当ではないと思います。完了ブロックは常に呼び出されます。また、強いセルフを使用する場合、完了ブロックが実行されるまでオブジェクトは割り当て解除されません。

ただし、[weak self]を使用する場合、オブジェクトは完了ブロックによって(一時的に)保持されず、完了ブロックが実行される前に割り当て解除される可能性があります。完了ブロックはまだ発生していますが、selfはすでにnilです。

完了ハンドラで[unowned self]を使用すると、完了ハンドラが呼び出される前にオブジェクトの割り当てが解除される可能性があり、クラッシュの原因となる可能性があります。

これを説明する例を作りました。

[gif illustrating the issue

完全なソースは次のようになります Githubにあります

15
Tieme

@Plaboは@Kirsteinsが言ったように、アニメーションと完了は自分で保持されないので、アニメーションを開始した場合や何らかの理由でビューコントローラが解放された場合でも、即座に割り当てが解除されます。したがって、キャプチャされた「自己」は必要ありません。以下の愚かな例を考えてみましょう:

class ViewController: UIViewController {

    @IBOutlet weak var button : UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        print("viewDidLoad ViewController")
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        UIView.animate(withDuration: 20, animations: {
            self.button.frame = CGRect(x: 0, y: 300, width: 30, height: 30)
        }) { finished in
            self.button.frame = CGRect(x: 0, y: 100, width: 30, height: 30)
        }
    }

    deinit {
        print("deinit ViewController")
    }

}

割り当てが解除されるとすぐに、非イニシャライザが呼び出され、完了は実行されません。

6
lhmgrassi

ただ反対。 wantselfは、完了ブロックが呼び出されるのに十分な期間存在し続けます。したがって、selfを強くしてエスケープ完了ハンドラーを通じて保持することはgoodのことです。

通常、人々がweak selfは保持サイクルです。しかし、これはそうではありません。保持サイクルとは、selfselfを保持するクロージャを保持し、selfを解放できないためにリークが発生したときです。しかし、これはまったくそのような状況ではありません。クロージャ、したがってselfは保持されますが、selfでは保持されません!したがって、一時的にいくつかの保留が発生していますが、それはgoodであり、悪くはありません。

2
matt

アニメーション、GCDまたは補完ハンドラーで弱い/所有されていないものを使用する必要はありません:

それらによってキャプチャされた外部オブジェクト参照はfixed timeの間のみ保持されます。これは、実行されることを意味しますある時点で。この後、解放されるため、メモリリークにつながる参照サイクルの可能性はありません。

以前の答えが示唆するように、

アニメーションおよび完了が自己によって保持されていない場合、誰がそれらを保持しますか?

私はこれの文書的な証拠を見つけられませんでしたが、私が信じているのはそれらは自分自身によって保持されますが、一定の時間。この後、完了が実行されてセルフが解放され、セルフの割り当てが解除されます。

保持サイクルのシナリオでは、クロージャは自身によって、およびクロージャによって無期限に保持されます。これは、参照サイクルおよびメモリリークと見なされます。

0
Abhijith