3

// ガード ステートメントで let 最初に bool チェックを実行すると、コンパイル エラーが発生します

   self.action = {  [weak self] in
      guard let `self` = self, data.isJSON() else { return }

// 最初に bool チェックを行い、次に let を実行します

  self.action = {  [weak self] in
     guard data.isJSON(), let `self` = self else { return }

上記の 2 つのステートメントは、私には同等に思えます。最初のケースで機能しないのはなぜですか?

4

1 に答える 1

4

質問を最小限で完全で検証可能な例に再構築する

まず第一に、質問に最小限で完全かつ検証可能な例 (mvce)が含まれていることが望ましいことに注意してください。現在の形式では、次のようにはなりません。

  • クロージャーリストは無関係であり、ここで混乱するだけです
  • self同様に、未知(... コンテキストなし) は混乱を招くだけであり、この質問とは関係ありません。

代わりに、あなたの質問の mvce は、次の行に沿って構築された可能性があります。

func foo(bar: Int?) {
    // 1. why does this cause a compile time error?
    guard let baz = bar, true else { return }

    // 2. whereas this does not?
    guard true, let bax = bar else { return }
}

以下の回答では、元の質問のあいまいな例ではなく、この mvce について説明します。最後に、 / ステートメントでも同じ動作が見られるため、 /guardステートメントguard letは質問の核心と完全に関連している (一意である) わけではないことにも注意してください。以下の回答では、ステートメントを使用します。ifif letguard


では、上記 1. のコンパイル時エラーを引き換えることはできますか? そして、これら2つのステートメントは本当に同等ですか?

guard上記の関数の最初のステートメントのコンパイル時エラーfoo(...)は非常にわかりやすい

ブール条件ではwhere 、変数バインディングから分離する必要があります。

修正: に置き換え,ますwhere

これは、Language Guide - The Basics - Optional Bindingにも記載されています。

複数のオプションのバインディングを 1 つのifステートメント に含めwhere句を使用してBoolean条件をチェックできます。オプションのバインディングのいずれかの値が であるnilか、where句が に評価された場合、オプションのバインディングfalse全体が失敗したと見なされます。

したがって、orステートメントでオプションのバインディングの後に条件句を使用する場合は、条件句を前のオプションのバインディングから分離する句を使用する必要があります。guardifwhere

func foo(bar: Int?) {
    // 1. ok, compiles
    guard let baz = bar where true else { return }

    /* 2. or, include a conditional-clause prior to the
          optional binding, but is this really equivalent..? */
    guard true, let bax = bar else { return }
}

ただし、これら 2 つは実際には同等ではありません。

  • ステートメント 2. 上記によりguard、最初の条件節が次のようになる場合に備えて、ステートメントを短縮することができますfalse(その後、オプションのバインディングに進むのではなく、ステートメントのelseブロックに直接進みます)。guard
  • 一方、1. 上記では逆が可能です。オプションのバインディングが成功した場合にのみ、条件節をチェックしてください。

当然、どちらか一方を優先する場合があります。たとえば、条件節に非常に重い計算が含まれている場合、オプションのバインディングが成功することが確実でない限り、これらを実行したくない場合があります。

let heavyStuff: () -> Bool = { print("foo"); /* ... */ return true }

func foo(bar: Int?) {
    /* 1. call the heavyStuff boolean construct only if
          the optional binding succeeds */
    guard let baz = bar where heavyStuff() else { return }

    /* 2. possibly unnesessarily perform heavy boolean
          stuff prior to failing the optional binding */
    guard heavyStuff(), let bax = bar else { return }
}

あまり工夫されていない例は、次の条件節で (ここでは引数として) 正常にバインドされた変数を使用する場合です。

let integerStuff: (Int) -> Bool = { _ in /* ... */ return true }

func foo(bar: Int?) {
    /* 1. call the integerStuff boolean construct only if
          the optional binding succeeds, using the binded
          immutable as closure argument */
    guard let baz = bar where integerStuff(baz) else { return }

    /* 2. ... not really any good alternatives for such 
          flow if using this alternative */
    guard integerStuff(baz ?? 0), let bax = bar else { return }
}

最後に、技術的には、最初のオプションのバインディングを次の条件節で本当に分離したい場合は、条件節のキーワードとcase let組み合わせたダミー(オプションではない常に続く変数バインディング/代入)ステートメントを使用できることに注意してくださいwhere

let checkThis: () -> Bool = { /* ... */ return true }

func foo(bar: Int?) {
    // ...
    guard let baz = bar, case let _ = () where checkThis() else { return }
}

ただし、これは単にこの専門性を示すためのものです。実際には、where句を使用するだけです。

于 2016-05-27T13:01:17.280 に答える