Ключове слово охоронця Свіфта


197

Swift 2 представив guardключове слово, за допомогою якого можна забезпечити налаштування різних даних, готових до роботи. Приклад, який я побачив на цьому веб-сайті, демонструє функцію submitTapped:

func submitTapped() {
    guard username.text.characters.count > 0 else {
        return
    }

    print("All good")
}

Мені цікаво, чи використовувати guardщось інакше, ніж робити це старомодно, використовуючи ifумову. Це дає переваги, яких ви не змогли отримати, користуючись простим чеком?


Також дивіться охорону проти, якщо нехай питання
Мед

Перейдіть за посиланням medium.com/@pvpriya7/swift-guard-18e59c50c624
V

Відповіді:


368

Читаючи цю статтю, я помітив великі переваги, використовуючи Guard

Тут ви можете порівняти використання охорони на прикладі:

Це частина без охорони:

func fooBinding(x: Int?) {
    if let x = x where x > 0 {
        // Do stuff with x
        x.description
    }

    // Value requirements not met, do something
}
  1. Тут ви вводите бажаний код у всі умови

    Ви можете не одразу побачити проблему з цим, але ви можете уявити, наскільки це може заплутатися, якби воно було вкладене у численні умови, які необхідно виконати, перш ніж запускати ваші заяви

Спосіб очистити це - зробити кожен свій чек спочатку та вийти, якщо такі не виконані. Це дозволяє легко зрозуміти, які умови змусять цю функцію вийти.

Але тепер ми можемо використовувати охорону і бачимо, що можливо вирішити деякі проблеми:

func fooGuard(x: Int?) {
    guard let x = x where x > 0 else {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
    x.description
}
  1. Перевірка стану, який ви хочете, а не того, якого ви не хочете. Це знову схоже на твердження. Якщо умова не виконується, запускається інше оператор охорони, який вибивається з функції.
  2. Якщо умова проходить, необов'язкова змінна тут автоматично розкручується для вас в межах, в якій викликалася команда guard - в цьому випадку функція fooGuard (_ :).
  3. Ви рано перевіряєте наявність поганих справ, роблячи свою функцію більш зрозумілою та легшою у обслуговуванні

Ця ж картина справедлива і для необов'язкових значень:

func fooNonOptionalGood(x: Int) {
    guard x > 0 else {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
}

func fooNonOptionalBad(x: Int) {
    if x <= 0 {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
}

Якщо у вас все ще виникають питання, ви можете прочитати всю статтю: Заява охоронця Swift.

Підведенню

І нарешті, читаючи та тестуючи, я виявив, що якщо ви використовуєте охорону для розгортання будь-яких додаткових варіантів,

ці розпаковані значення залишаються навколо вас, щоб використовувати їх у решті коду

.

guard let unwrappedName = userName else {
    return
}

print("Your username is \(unwrappedName)")

Тут розгорнуте значення буде доступне лише всередині блоку if

if let unwrappedName = userName {
    print("Your username is \(unwrappedName)")
} else {
    return
}

// this won't work – unwrappedName doesn't exist here!
print("Your username is \(unwrappedName)")

3
Гей @Eric, ти чудово поставився! Дякуємо Вам за те, що зробили це так легко зрозуміти!
Хорхе Касаріего

1
Я використовую охорону для розгортання NSError. Але коли я намагаюся використовувати його в межах охорони (наприклад, для передачі помилки в якийсь зворотний виклик), він каже: "Змінна, оголошена в стані охорони, не може бути використана в її тілі". Це має сенс? Спасибі
GeRyCh

6
Розгортання @GeRyCh у операторі захисту робить цю змінну доступною після оператора захисту, а не в її межах. Зайняв мене час, щоб звикнути до цього.
DonnaLea

2
Ось ще одна чудова стаття про використання гвардії для чистого розгортання додаткових параметрів. Підсумовує це красиво.
Доче

let x = x where x > 0чи означає це, що ви поєднали ще одну умову у факультативному зв’язуванні? Я маю на увазі, це трохи відрізняється відif let constantName = someOptional { statements }
Honey

36

На відміну від цього if, guardстворює змінну, до якої можна отримати доступ поза її блоком. Корисно розгортати багато Optionalс.


24

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

Інша перевага часто - це логіка, яку ви хочете реалізувати, більше " if not let", ніж " if let { } else".

Ось приклад: припустимо, що ви хочете реалізувати accumulate- зменшується міжряддя mapта те, reduceде він повертає масив запущеного масиву . Ось це з guard:

extension Sliceable where SubSlice.Generator.Element == Generator.Element {

    func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {
        // if there are no elements, I just want to bail out and
        // return an empty array
        guard var running = self.first else { return [] }

        // running will now be an unwrapped non-optional
        var result = [running]

        // dropFirst is safe because the collection
        // must have at least one element at this point
        for x in dropFirst(self) {
            running = combine(running, x)
            result.append(running)
        }
        return result
    }

}


let a = [1,2,3].accumulate(+)  // [1,3,6]
let b = [Int]().accumulate(+)  // []

Як би ви написали це без охорони, але все ж за допомогою firstцього повертається необов’язково? Щось на зразок цього:

extension Sliceable where SubSlice.Generator.Element == Generator.Element {

    func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {

        if var running = self.first  {
            var result = [running]

            for x in dropFirst(self) {
                running = combine(running, x)
                result.append(running)
            }
            return result
        }
        else {
            return []
        }
    }

}

Додаткове гніздування дратує, але також не так логічно мати ifі elseтак далеко один від одного. Набагато легше читати ранній вихід для порожнього корпусу, а потім продовжувати роботу з рештою функції, як ніби це не було можливим.


19

Коли умова виконується за допомогою, guardвона піддає змінним, оголошеним всередині guardблоку, решті блоку коду, приводячи їх у свою область застосування. Що, як було сказано раніше, неодмінно стане у нагоді з вкладеними if letтвердженнями.

Зауважте, що охоронець вимагає повернення чи кидка у своїй заяві else.

Розбір JSON з охороною

Нижче наводиться приклад того, як можна проаналізувати об’єкт JSON, використовуючи охорону, а не якщо дозволяти. Це уривок із запису в блозі, який включає файл ігрового майданчика, який ви можете знайти тут:

Як використовувати Guard в Swift 2 для розбору JSON

func parseJSONWithGuard(data : [String : AnyObject]) throws -> Developer {

    guard let firstname = data["First"] as? String  else {
        return Developer() // we could return a nil Developer()
    }

    guard let lastname = data["Last"] as? String else {
        throw ParseError.BadName // or we could throw a custom exception and handle the error
    }

    guard let website = data["WebSite"] as? String else {
        throw ParseError.BadName
    }

    guard let iosDev = data["iosDeveloper"] as? Bool else {
        throw ParseError.BadName
    }



    return Developer(first: firstname, last: lastname, site: website, ios: iosDev)

}

завантажити майданчик: майданчик для охорони

Більше інформації:

Ось уривок із посібника з мов програмування Swift:

Якщо умова твердження про захист виконана, виконання коду продовжується після завершального діапазону оператора захисту. Будь-які змінні або константи, яким було присвоєно значення, використовуючи необов'язкове прив'язування, як частину умови, доступні для решти блоку коду, в якому відображається оператор захисту.

Якщо ця умова не виконується, виконується код всередині іншої гілки. Ця гілка повинна передати управління для виходу з кодового блоку, у якому з'являється цей захисний вислів. Це можна зробити за допомогою оператора передачі управління, такого як повернення, перерва або продовження, або він може викликати функцію або метод, який не повертається, наприклад як fatalError ().


7

Одна перевага - це усунення багатьох вкладених if letзаяв. Дивіться відео про WWDC "Що нового у Свіфті" близько 15:30, розділ під назвою "Піраміда приреченості".


6

Коли користуватися охоронцями

Якщо у вас є контролер перегляду з кількома елементами UITextField або яким-небудь іншим типом введення користувача, ви відразу помітите, що вам потрібно розкрутити textField.text, щоб отримати текст всередині нього (якщо такий є!). isEmpty не принесе вам нічого корисного, без будь-якого введення текстове поле просто поверне нуль.

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

func submit() {
    guard let name = nameField.text else {
        show("No name to submit")
        return
    }

    guard let address = addressField.text else {
        show("No address to submit")
        return
    }

    guard let phone = phoneField.text else {
        show("No phone to submit")
        return
    }

    sendToServer(name, address: address, phone: phone)
}

func sendToServer(name: String, address: String, phone: String) {
  ...
}

Ви помітите, що наша серверна комунікаційна функція приймає як необов'язкові значення рядка як параметри, отже, попередньо розкручуючи захист. Розгортання трохи неінтуїтивно, тому що ми звикли розгортати, якщо нехай розгортає значення для використання всередині блоку. Тут оператор guard має пов'язаний блок, але це насправді інший блок - тобто те, що ви робите, якщо розгортання не вдалося - значення розгортаються прямо в той же контекст, що і сам вислів.

// відокремлення проблем

Без охорони

Не використовуючи охорону, ми закінчилися з великою купою коду, що нагадує піраміду приреченості . Це не дуже добре для додавання нових полів до нашої форми або створення дуже читабельного коду. Відступ може бути важким для дотримання, особливо з такою кількістю інших тверджень на кожному вилку.

func nonguardSubmit() {
    if let name = nameField.text {
        if let address = addressField.text {
            if let phone = phoneField.text {
                sendToServer(name, address: address, phone: phone)
            } else {
                show("no phone to submit")
            }
        } else {
            show("no address to submit")
        }
    } else {
        show("no name to submit")
    }
}

Так, ми могли б навіть об'єднати все це, якби дозволити висловлювання в одне твердження, розділене комами, але ми втратимо можливість зрозуміти, який звіт не вдався і подати повідомлення користувачеві.

https://thatthinginswift.com/guard-statement-swift/


5

З використанням охорони наше наміри зрозуміло. ми не хочемо виконувати решту коду, якщо ця конкретна умова не виконується. тут ми також можемо розширити ланцюжок, будь ласка, подивіться нижче код:

guard let value1 = number1, let value2 = number2 else { return }
 // do stuff here

5

Заява охоронця збирається зробити. це пара різних

1) це дозволить мені зменшити вкладені, якщо заява
2) це збільшити міру, яку міняє доступна моя змінна

якщо заява

func doTatal(num1 : Int?, num2: Int?) {
  // nested if statement
    if let fistNum = num1 where num1 > 0 {
        if let lastNum = num2 where num2 < 50 {

          let total = fistNum + lastNum
        }
    }
 // don't allow me to access out of the scope 
 //total = fistNum + lastNum 
}

Заява гвардії

func doTatal(num1 : Int?, num2: Int?) {
   //reduce  nested if statement and check positive way not negative way 
    guard let fistNum = num1 where num1 > 0 else{
      return
    }
    guard  let lastNum = num2 where num2 < 50 else {
     return
    }
    // increase my scope which my variable accessible
    let total = fistNum + lastNum

}

3

З документації Apple:

Заява про охорону

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

Синатекс:

guard condition else {
    statements
}

Перевага:

1. За допомогою guardоператора ми можемо позбутися глибоко вкладених умов, єдиною метою яких є перевірка набору вимог.

2. Він був розроблений спеціально для раннього виходу з методу чи функції.

якщо ви використовуєте, якщо нехай нижче код - як це виглядає.

  let task = URLSession.shared.dataTask(with: request) { (data, response, error) in

        if error == nil {
            if let  statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 {
                if let data = data {

                    //Process Data Here.
                    print("Data: \(data)")

                } else {
                    print("No data was returned by the request!")
                }
            } else {
                print("Your request returned a status code other than 2XX!")
            }
        } else {
            print("Error Info: \(error.debugDescription)")
        }
    }
    task.resume()

Використовуючи захист, ви можете перенести контроль за межі сфери, якщо не виконано одну або більше умов.

let task = URLSession.shared.dataTask(with: request) { (data, response, error) in

            /* GUARD: was there an error? */
            guard (error == nil) else {
                print("There was an error with your request: \(error)")
                return
            }

            /* GUARD: Did we get a successful 2XX response? */
            guard let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 else {
                print("Your request returned a status code other than 2XX!")
                return
            }

            /* GUARD: was there any data returned? */
            guard let data = data else {
                print("No data was returned by the request!")
                return
            }

            //Process Data Here.
            print("Data: \(data)")
}
task.resume()

Довідка:

1. Швидкий 2: Рано вийдіть із охороною 2. Ударіті 3. Заява охоронця


Але ви можете зробити останнє за допомогою тверджень? if condition { return }похмурий?
Олівер Діксон

2

Як і якщо оператор if, охоронник виконує висловлювання на основі булевого значення виразу. На відміну від заяви if, заяви захисту виконуються лише в тому випадку, якщо умови не виконані. Ви можете думати про охорону більше, як Assert, але замість того, щоб розбитися, ви можете витончено вийти.

зверніться до: http://ericcerney.com/swift-guard-statement/


1

Це дійсно дійсно робить потік послідовності з кількома пошуковими запитами та необов’язковістю набагато більш стислим та зрозумілим, а також зменшує безліч вкладень. Див. Публікацію Еріки Садун про заміну ifs . .... Не можу захопитися, приклад нижче:

    let filteredLinks = locationsLinkedToList.filter({$0.actionVerb == movementCommand})
    guard let foundLink = filteredLinks.first else {return ("<Person> cannot go in that direction.", nil, nil)}
    guard filteredLinks.count == 1 else {return ("<Person> cannot decide which route to take.", nil, nil)}
    guard let nextLocation = foundLink.toLocation else {return ("<Person> cannot go in that direction.", nil, nil)}

Подивіться, чи це палиця.


1

Простіше кажучи, він пропонує спосіб перевірити поля перед виконанням. Це хороший стиль програмування, оскільки він підвищує читабельність. На інших мовах це може виглядати приблизно так:

func doSomething() {
    if something == nil {
        // return, break, throw error, etc.
    }
    ...
}

Але оскільки Swift надає додаткові опції, ми не можемо перевірити, чи є нульовим, і призначити його значення змінній. Навпаки, if letперевіряє, що це не нульове значення, і призначає змінну для утримування фактичного значення. Ось тут і guardвступає в гру. Це дає вам більш стислий спосіб виходу з раннього використання додаткових варіантів.


1

Джерело: Guard in Swift

Подивимось приклад, щоб зрозуміти це чітко

Приклад 1:

func validate() {         
    guard 3>2 else {             
    print ("False")             
    return         
    }         
    print ("True") //True     
} 
validate()

У наведеному вище прикладі ми бачимо, що 3 більше, ніж 2, і твердження, що знаходиться в пункті вартості іншого, пропускається, і надрукується True.

Приклад 2:

func validate() {         
    guard 1>2 else {             
    print ("False")            //False 
    return         
    }         
    print ("True")      
} 
validate()

У наведеному вище прикладі ми бачимо, що 1 менше, ніж 2, і заява всередині пункту guard else виконується, і False друкується з подальшим поверненням.

Example 3: gaurd let, unwrapping optionals through guard let

func getName(args myName: String?) {
     guard let name = myName, !name.isEmpty else {
     print ("Condition is false")          // Condition is false            return         
     }         
     print("Condition is met\(name)")     
} 
getName(args: "")

У наведеному вище прикладі ми використовуємо гвардію, щоб розгортати додаткові параметри. У функції getName ми визначили змінну рядка типу myName, яка не є обов'язковою. Тоді ми використовуємо гвардію, щоб перевірити, чи змінна myName нульова чи ні, якщо не призначити ім'я та перевірити ще раз, чи ім’я не порожнє. Якщо обидві умови кваліфіковані, тобто істинні, блок else буде пропущений і надрукувати "Умови відповідають імені".

В основному ми перевіряємо дві речі, розділені комою, спочатку розкручуючи та необов'язково та перевіряючи, чи відповідає це умові чи ні.

Тут ми нічого не передаємо до функції, тобто порожній рядок, а значить, умова є помилковим - це print.

func getName(args myName: String?) {
     guard let name = myName, !name.isEmpty else {
     print ("Condition is false")          
     return         
     }        
     print("Condition is met \(name)") // Condition is met Hello    
} getName(args: "Hello")

Тут ми передаємо функцію "Привіт", і ви бачите, що результат друкується "Умова виконано Привіт".

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