Swift: защита против, если пусть

Я читал о опциях в Swift, и я видел примеры, где if let используется для проверки того, имеет ли значение Option значение, а в случае его действия - что-то делать с развернутым значением.

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

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

Ответ 1

if let и guard let служат одинаковые, но разные цели.

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

if let определяет его область действия и не требует ничего особенного. Он может return или нет.

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

Ответ 2

Страж может улучшить ясность

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

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 you write the above code with if-let it conveys to the reading developer that it more of a 50-50. But if you use guard you add clarity to your code и it implies я expect this to work 95% of the time...if it ever failed, я don't know why it would; it very unlikely...but then just use this default image instead or perhaps just assert with a meaningful message describing what went wrong!

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

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

From: Erica Sadun Swift Style book

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

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

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

From: Erica Sadun Swift Style book + some modifications

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

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


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

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

Однако и guard let, и if let распаковывают переменную

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

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

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 does not exist!
    }
    if let blogName1 = blog { // No Error at this line! Because blogName only exists inside the code block ie {}
        print(blogName1)
    }
}

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


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

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

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

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!
}

Ответ 3

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

Скажем, у вас есть func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int и необязательный массив элементов (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-let по мере приближения к завершению проверки, "путь успеха" и теперь успешно привязанные дополнительные параметры находятся в основной области метода, поскольку все пути возврата уже возвращены.

Ответ 4

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

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

Если какое-либо 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)
}

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

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

Ответ 5

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

Охранник пусть

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

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

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

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

Ответ 6

Самое ясное объяснение, которое я видел, было в Руководство по стилю Github Swift:

if добавляет уровень глубины:

if n.isNumber {
    // Use n here
} else {
    return
}

guard не:

guard n.isNumber else {
    return
}
// Use n here

Ответ 7

охрана

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

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

Охранное выражение имеет следующую форму:

guard condition else {
    //Generally return
}

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

if let roomCount = optionalValue {
       print("roomCount available")
    } else {
        print("roomCount is nil")
 }

Ответ 8

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

Типичное остальное-если

 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 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 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")
                         }
                  }

Код выше, безусловно, работает, но нарушает принцип СУХОЙ. Это зверское. Давайте разберемся с этим. +

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

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 Остальные операторы 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 позволяет нам разворачивать несколько опций одновременно. Если один из них содержит ноль, он выполнит блок 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")
  }
}

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

Разверните несколько опций с помощью Guard Конечно, мы должны использовать 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
}