Swift: охранник пусть против, если пусть

133

Я читал о Optionals в Swift и видел примеры, в которых if letон используется, чтобы проверить, содержит ли Optional значение, и, если это так, сделайте что-нибудь с развернутым значением.

Однако я видел, что в Swift 2.0 в guard letосновном используется ключевое слово . Интересно, было ли if letудалено из Swift 2.0 или его все еще можно использовать.

Следует ли мне изменить свои программы, содержащие if letв guard let?

lmiguelvargasf
источник

Ответы:

165

if letи guard letслужат аналогичным, но различным целям.

Случай "else" guardдолжен выйти из текущей области. Обычно это означает, что он должен вызвать returnили прервать программу. guardиспользуется для обеспечения раннего возврата без необходимости вложения остальной части функции.

if letгнездится в своем объеме и не требует от него ничего особенного. Может returnили нет.

В общем, если if-letблок будет остальной частью функции или в его elseпредложении будет returnили прерываться, тогда вы должны использовать guardвместо этого. Это часто означает (по крайней мере, по моему опыту), когда есть сомнения, guardэто обычно лучший ответ. Но есть масса ситуаций, когда if letвсе же уместно.

Роб Напье
источник
39
Используйте, if letкогда non-nilслучай действителен. Используйте, guardкогда nilрегистр представляет собой какую-то ошибку.
BallpointBen
4
@BallpointBen Я не согласен с этим. Есть много случаев, когда guardэто уместно, даже если ошибки нет. Иногда это просто означает, что делать нечего. Например, positionTitleметод может guard if let title = title else {return}. Заголовок может быть необязательным, и в этом случае это не ошибка. Но guard letвсе равно уместно.
Роб Напье
1
Да; Я имел в виду охранник, впустивший комментарий.
Роб Напье
1
другими словами, "guard let" используется, когда код на 99% уверен, что не использует условие else; с другой стороны, «если разрешить», когда код равен 50–50 (пример), чтобы использовать условие else.
Chino Pan
1
Связанная переменная if letвидна только внутри if let области видимости. После этого guard letбудет видна переменная, связанная с . Поэтому имеет смысл использовать guard для привязки необязательных значений.
Boweidmann
106

Охрана может улучшить четкость

Когда вы используете охранник, у вас гораздо больше ожиданий, что охранник добьется успеха, и несколько важно, что если он не удастся, вы просто захотите выйти из области видимости раньше . Как вы охраняете, существует ли файл / изображение, является ли массив пустым или нет.

func icon() -> UIImage {
    guard let image = UIImage(named: "Photo") else {
        return UIImage(named: "Default")! //This is your fallback
    }
    return image //-----------------you're always expecting/hoping this to happen
}

Если вы напишете приведенный выше код с помощью if-let, он покажет читающему разработчику, что это больше 50-50. Но если вы используете guard, вы добавляете ясности в свой код, и это означает, что я ожидаю, что это сработает в 95% случаев ... если это когда-нибудь не удастся, я не знаю, почему это произойдет; это очень маловероятно ... но тогда просто используйте это изображение по умолчанию или, возможно, просто подтвердите с осмысленным сообщением, описывающим, что пошло не так!

  • Избегайте guardпоявления побочных эффектов, охранники должны использоваться как естественный поток. Избегайте осторожности, когда elseстатьи вызывают побочные эффекты. Охранники устанавливают необходимые условия для правильного выполнения кода, предлагая ранний выход

  • Когда вы выполняете значительные вычисления в положительной ветви, выполняйте рефакторинг от ifдо guardоператора и возвращает резервное значение в elseпредложении

Из: Книга Эрики Садун "Быстрый стиль"

Кроме того, в результате приведенных выше предложений и чистого кода более вероятно, что вы захотите / должны будете добавлять утверждения в неудавшиеся операторы защиты, это просто улучшает читаемость и дает другим разработчикам понять, чего вы ожидали.

guard​ ​let​ image =UIImage(named: selectedImageName) else { // YESSSSSS
     assertionFailure("Missing ​​\(​selectedImageName​)​​ asset") 
     return
} 

guard​ ​let​ image =UIImage(named: selectedImageName) else { // NOOOOOOO
​     ​return 
}

Из: Книга Эрики Садун Swift Style + некоторые модификации

(вы не будете использовать утверждения / предварительные условия для if-lets. Это просто не кажется правильным)

Использование охранников также поможет вам улучшить ясность, избегая пирамиды гибели. См . Ответ Нитина .


Guard создает новую переменную

Есть одно важное различие, которое, как мне кажется, никто не объяснил.

Оба guard letи if let развернуть переменную, однако

С помощью guard letвы создаете новую переменную, которая будет существовать вне elseоператора.

При этом if letвы не создаете никакой новой переменной - после оператора else вы вводите блок кода, только если необязательный параметр не равен нулю. Вновь созданная переменная существует только внутри блока кода, а не после!

guard let:

func someFunc(blog: String?) {

    guard let blogName = blog else {
        print("some ErrorMessage")
        print(blogName) // will create an error Because blogName isn't defined yet
        return
    }
    print(blogName) // You can access it here ie AFTER the guard statement!!

    //And if I decided to do 'another' guard let with the same name ie 'blogName' then I would create an error!
    guard let blogName = blog else { // errorLine: Definition Conflicts with previous value.
        print(" Some errorMessage")
        return
    }
    print(blogName)
}

if-let:

func someFunc(blog: String?) {


    if let blogName1 = blog {
        print(blogName1) // You can only access it inside the code block. Outside code block it doesn't exist!
    }
    if let blogName1 = blog { // No Error at this line! Because blogName only exists inside the code block ie {}
        print(blogName1)
    }
}

Для получения дополнительной информации if letсм .: Почему повторное объявление необязательной привязки не приводит к ошибке


Охранник требует выхода из прицела

(Также упоминается в ответе Роба Напьера):

Вы ДОЛЖНЫ guardопределить внутри func. Его основная цель - прервать / вернуть / выйти из области действия, если условие не выполняется:

var str : String?

guard let blogName1 = str else {
    print("some error")
    return // Error: Return invalid outside of a func
}
print (blogName1)

Поскольку if letвам не нужно иметь его внутри какой-либо функции:

var str : String?    
if let blogName1 = str {
   print(blogName1) // You don't get any errors!
}

guard против if

Стоит отметить , что это более уместно , чтобы увидеть этот вопрос , как guard letпротив if letи guardпротив if.

Автономная версия ifне выполняет разворачивания, как и автономная версия guard. См. Пример ниже. Если есть значение, он не выходит раньше срока nil. Нет дополнительных значений. Он просто выходит рано, если условие не выполняется.

let array = ["a", "b", "c"]
func subscript(at index: Int) -> String?{
   guard index > 0, index < array.count  else { return nil} // exit early with bad index
   return array[index]
}
Мед
источник
46

Когда if-letи когда использовать guard- это часто вопрос стиля.

Скажем, у вас есть func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Intи необязательный массив items ( var optionalArray: [SomeType]?), и вам нужно вернуть либо, 0если массив nil(не установлен), либо countесли массив имеет значение (установлено).

Вы можете реализовать это так, используя if-let:

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        if let array = optionalArray {
            return array.count
        }
        return 0
    }

или вот так, используя guard:

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        guard let array = optionalArray else {
            return 0
        }
        return array.count
    }

Примеры функционально идентичны.

Что guardдействительно хорошо, так это когда у вас есть такая задача, как проверка данных, и вы хотите, чтобы функция вылетала раньше, если что-то пойдет не так.

Вместо того, чтобы вкладывать кучу if-lets по мере приближения к завершению проверки, «путь успеха» и теперь успешно связанные опции находятся в основной области действия метода, потому что все пути отказа уже вернулись.

divergio
источник
30

Я попытаюсь объяснить полезность операторов защиты с помощью некоторого (неоптимизированного) кода.

У вас есть пользовательский интерфейс, в котором вы проверяете текстовые поля для регистрации пользователя с именем, фамилией, адресом электронной почты, телефоном и паролем.

Если какое-либо поле textField не содержит допустимого текста, оно должно сделать это поле firstResponder.

вот неоптимизированный код:

//pyramid of doom

func validateFieldsAndContinueRegistration() {
    if let firstNameString = firstName.text where firstNameString.characters.count > 0{
        if let lastNameString = lastName.text where lastNameString.characters.count > 0{
            if let emailString = email.text where emailString.characters.count > 3 && emailString.containsString("@") && emailString.containsString(".") {
                if let passwordString = password.text where passwordString.characters.count > 7{
                    // all text fields have valid text
                    let accountModel = AccountModel()
                    accountModel.firstName = firstNameString
                    accountModel.lastName = lastNameString
                    accountModel.email = emailString
                    accountModel.password = passwordString
                    APIHandler.sharedInstance.registerUser(accountModel)
                } else {
                    password.becomeFirstResponder()
                }
            } else {
                email.becomeFirstResponder()
            }
        } else {
            lastName.becomeFirstResponder()
        }
    } else {
        firstName.becomeFirstResponder()
    }
}

Вы можете видеть выше, что все строки (firstNameString, lastNameString и т. Д.) Доступны только в рамках оператора if. поэтому он создает эту «пирамиду гибели» и имеет много проблем с ней, включая удобочитаемость и простоту перемещения (если порядок полей изменяется, вам придется переписать большую часть этого кода)

С помощью оператора guard (в приведенном ниже коде) вы можете видеть, что эти строки доступны за пределами {}и используются, если все поля действительны.

// guard let no pyramid of doom
func validateFieldsAndContinueRegistration() {

guard let firstNameString = firstName.text where firstNameString.characters.count > 0 else {
            firstName.becomeFirstResponder()
            return
        }
guard let lastNameString = lastName.text where lastNameString.characters.count > 0 else {
            lastName.becomeFirstResponder()
            return
        }
guard let emailString = email.text where 
        emailString.characters.count > 3 &&
        emailString.containsString("@") && 
        emailString.containsString(".") else {
            email.becomeFirstResponder()
            return
        }
guard let passwordString = password.text where passwordString.characters.count > 7 else {
            password.becomeFirstResponder()
            return
        }

// all text fields have valid text
    let accountModel = AccountModel()
    accountModel.firstName = firstNameString
    accountModel.lastName = lastNameString
    accountModel.email = emailString
    accountModel.password = passwordString
    APIHandler.sharedInstance.registerUser(accountModel)
}

Если порядок полей изменится, просто переместите соответствующие строки кода вверх или вниз, и все готово.

Это очень простое объяснение и вариант использования. Надеюсь это поможет!

Нитин Алабур
источник
14

Основная разница

Охрана пусть

  1. Ранний существующий процесс из области
  2. Требовать наличия очков, таких как возврат, бросок и т. Д.
  3. Создайте новую переменную, к которой можно получить доступ вне области видимости.

если позволите

  1. Нет доступа к прицелу.
  2. нет необходимости возвращать заявление. Но мы можем написать

ПРИМЕЧАНИЕ. Оба используются для развертывания необязательной переменной.

Kiran K
источник
2

охрана

  • guardОператор используется для передачи программы управления из области видимости , если одно или несколько условий не выполняются.

  • Значение любого условия в guardоператоре должно иметь тип Bool или тип, к которому он подключен Bool. Условие также может быть необязательным объявлением привязки.

Заявление охранника имеет следующую форму:

guard condition else {
    //Generally return
}

если позволите

if let roomCount = optionalValue {
    print("roomCount available")
} else {
    print("roomCount is nil")
}
Акшай
источник
1

Я научился этому у Свифта с Бобом ..

Типичное «Иначе если»

 func checkDrinkingAge() {
      let canDrink = true

     if canDrink {
        print("You may enter")
       // More Code
        // More Code
      // More Code

         } else {
         // More Code
    // More Code
    // More Code
    print("Let me take you to the jail")
          }
     }

Проблемы с Else-If

  1. Вложенные скобки
  2. Необходимо прочитать каждую строку, чтобы обнаружить сообщение об ошибке

Заявление защиты Блок защиты выполняется только в том случае, если условие ложно, и он выйдет из функции через return. Если условие истинно, Swift игнорирует блокировку защиты. Обеспечивает ранний выход и меньшее количество скобок.

func checkDrinkProgram() {
       let iCanDrink = true

           guard iCanDrink else {
        // if iCanDrink == false, run this block
         print("Let's me take you to the jail")
          return
        }

         print("You may drink")
           // You may move on
                  // Come on.
                 // You may leave
                // You don't need to read this.
                 // Only one bracket on the bottom: feeling zen.
       }

Разверните опции с помощью Else-If

Оператор защиты не только полезен для замены типичного условного блока оператором else-if, но также отлично подходит для разворачивания опций за счет минимизации количества скобок. Для сравнения давайте сначала начнем с того, как развернуть несколько опций с помощью else-if. Во-первых, давайте создадим три варианта, которые будут развернуты.

var publicName: String? = "Bob Lee"
var publicPhoto: String? = "Bob's Face"
var publicAge: Int? = nil

Худший кошмар

func unwrapOneByOne() {
         if let name = publicName {
              if let photo = publicPhoto {
                     if let age = publicAge {
                        print("Bob: \(name), \(photo), \(age)")
                                  } else {
                          print("age is mising")
                           }
                  } else {
                      print("photo is missing")
                         }
                  } else {
                        print("name is missing")
                         }
                  }

Приведенный выше код определенно работает, но нарушает принцип DRY. Это ужасно. Разобьем это +.

Немного лучше Код ниже более читабелен, чем приведенный выше.

func unwrapBetter() {
         if let name = publicName {
       print("Yes name")
                   } else {
               print("No name")
        return
      }

         if let photo = publicPhoto {
             print("Yes photo")
            } else {
           print("No photo")
       return
             }

        if let age = publicAge {
            print("Yes age")
                      } else {
                print("No age")
            return
                           }
     }

Распаковать с помощью Guard Операторы else-if можно заменить на guard.

 func unwrapOneByOneWithGuard() {
             guard let name = publicName else {
                  print("Name missing")
              return
                                        }

              guard let photo = publicPhoto else {
              print("Photo missing")
                return
                                            }

                  guard let age = publicAge else {
                   print("Age missing")
                                     return
                                                 }
                 print(name)
                 print(photo)
                 print(age)
         }

Развертывание нескольких опций с помощью Else-If До сих пор вы разворачивали опционы один за другим. Swift позволяет нам развернуть сразу несколько опций. Если один из них содержит nil, он выполнит блок else.

func unwrap() {
  if let name = publicName, let photo = publicPhoto, let age = publicAge {
    print("Your name is \(name). I see your face right here, \(photo), you are \(age)")
  } else {
    // if any one of those is missing
    print("Something is missing")
  }
}

Имейте в виду, что когда вы разворачиваете сразу несколько опций, вы не можете определить, какие из них содержат nil

Разверните несколько опций с помощью Guard. Конечно, мы должны использовать защиту от else-if. +

func unwrapWithGuard() {
  guard let name = publicName, let photo = publicPhoto, let age = publicAge else {
    // if one or two of the variables contain "nil"
    print("Something is missing")
    return
  }

  print("Your name is \(name). I see your, \(photo). You are \(age).")
  // Animation Logic
  // Networking
  // More Code, but still zen
}
Уранг-Зеб Хан
источник
пожалуйста, вернитесь и исправьте форматирование кода / отступы!
pkamb