Свіфт: охоронець нехай проти, якщо нехай


132

Я читав про "Необов’язкові" у Swift, і бачив приклади, коли if letвикористовується для перевірки, чи має необов'язкове значення, а в разі, якщо це так, - зробіть щось із розпакованим значенням.

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

Повинен чи я змінити свої програми , які містять if letв guard let?

Відповіді:


164

if letі guard letслужать подібними, але чіткими цілями.

Випадок "else" guardповинен вийти з поточної області. Взагалі це означає, що він повинен зателефонувати returnабо перервати програму. guardвикористовується для забезпечення раннього повернення, не вимагаючи введення решти функції.

if letгніздиться своїм розмахом і не вимагає від нього нічого особливого. Це може returnчи ні.

Взагалі, якщо if-letблок буде рештою функції, або в його elseпункті міститься returnабо припиняється, тоді вам слід скористатися guardзамість цього. Це часто означає (принаймні, на мій досвід), коли сумніваються, guardяк правило, краща відповідь. Але є маса ситуацій, коли if letвсе-таки це доречно.


38
Використовуйте, if letколи non-nilсправа дійсна. Використовуйте, guardколи nilвипадок представляє якусь помилку.
BallpointBen

4
@BallpointBen Я не згоден з цим. Є багато випадків, коли guardце доречно, навіть якщо помилок немає. Іноді це просто означає, що робити нічого. Наприклад, positionTitleметод може бути guard if let title = title else {return}. Заголовок може бути необов’язковим, і в цьому випадку це не помилка. Але guard letвсе ж підходить.
Роб Нап'єр

1
Так; Я мав на увазі охоронець, нехай в коментарі.
Роб Нап'єр

1
інакше кажучи, "захист нехай" використовується, коли код на 99% впевнений, що не використовує інше умовне; з іншого боку, "якщо дозволити", коли код 50 - 50 (приклад), щоб використовувати іншу умову.
Пан Чино

1
Змінна, з якою пов'язана, if letвидно лише всередині if let області. Змінна, зв'язана знаком, guard letвидно згодом. Тож має сенс використовувати і захист для прив’язки необов'язкових значень.
boweidmann

105

Охорона може підвищити чіткість

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

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-нехай він повідомляє розробнику читання, що він більше 50-50. Але якщо ви використовуєте охорону, ви додаєте чіткість своєму коду, і це означає, що я очікую, що це спрацює 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 + деякі модифікації

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

Використання охоронців також допоможе вам підвищити чіткість, уникаючи піраміди приреченості. Дивіться відповідь Нітіна .


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і необов'язковий масив елементів ( 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, коли ви наближаєтесь до завершення перевірки, "шлях успіху" та вже успішно пов'язані додаткові опції - все це в основній області методу, тому що шляхи відмови вже повернулися.


30

Я спробую пояснити корисність заяв про охорону деяким (неоптимізованим) кодом.

У вас є інтерфейс користувача, де ви перевіряєте текстові поля для реєстрації користувача з іменем, прізвищем, електронною поштою, телефоном та паролем.

Якщо будь-яке текстове поле не містить дійсного тексту, воно повинно зробити це поле першим Відповідачем.

ось неоптимізований код:

//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 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. не потрібно повертати заяву. Але ми можемо написати

ПРИМІТКА. Обидва використовуються для розгортання необов'язкової змінної.



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. Доводиться читати кожен рядок, щоб помітити повідомлення про помилку

Заява про охорону Блок захисту працює лише в тому випадку, якщо умова помилкова, і він вийде з функції через повернення. Якщо умова справжня, Свіфт ігнорує блок охорони. Він забезпечує швидкий вихід та меншу кількість дужок. +

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, а інші - якщо заяви можна замінити на 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")
  }
}

Майте на увазі, що розгортаючи кілька необов’язків одночасно, ви не можете визначити, що містить нуль

Розгортайте декілька необов’язків із охороною Звичайно, ми повинні використовувати охорону за іншим, якщо +

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
}

поверніться та виправте форматування / відступ коду!
pamamb
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.