Ключевое слово Свифта

197

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

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

    print("All good")
}

Мне интересно, если использование guardотличается от того, как делать это по старинке, используя ifусловие. Это дает преимущества, которые вы не могли получить, используя простую проверку?

Дэвид Снабель
источник
Также см. Вопрос « гвардия против если-давай»
Honey
Пожалуйста,
Приянка V

Ответы:

369

Читая эту статью, я заметил большие преимущества использования 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. Проверка на состояние, которое вы хотите, а не то, которое вы не делаете. Это снова похоже на утверждение. Если условие не выполняется, выполняется оператор else оператора защиты, который выходит из функции.
  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.

Завершение

И, наконец, читая и тестируя, я обнаружил, что если вы используете охрану, чтобы развернуть какие-либо дополнительные функции,

эти развернутые значения остаются для вас, чтобы использовать в остальной части вашего блока кода

,

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
Эй, Эрик, ты сделал отличный пост! Спасибо за то, что вы так легко понимаете!
Хорхе Казариего
1
Я использую охрану для распаковки NSError. Но когда я пытаюсь использовать его в пределах защиты (например, для передачи ошибки в некоторый обратный вызов), он говорит: «Переменная, объявленная в условии защиты, не может использоваться в своем теле». Есть ли смысл? Спасибо
GeRyCh
6
Развертывание @GeRyCh в защитном операторе делает эту переменную доступной после защитного оператора, а не внутри нее. Мне понадобилось время, чтобы привыкнуть к этому.
DonnaLea
2
Вот еще одна отличная статья об использовании охраны для чистой развертки опций. Подводит итоги приятно.
Доше
let x = x where x > 0означает ли это, что вы включили другое условие в необязательную привязку? Я имею в виду, что это немного отличается отif let constantName = someOptional { statements }
Honey
36

В отличие от if, guardсоздает переменную, к которой можно получить доступ за пределами ее блока. Это полезно, чтобы развернуть много Optionalс.

Takebayashi
источник
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, а не if-let. Это отрывок из записи блога, которая включает в себя файл игровой площадки, который вы можете найти здесь:

Как использовать 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:

Если условие оператора охраны выполнено, выполнение кода продолжается после закрывающей скобки оператора охраны. Любые переменные или константы, которым были присвоены значения с использованием необязательной привязки как части условия, доступны для остальной части блока кода, в котором появляется оператор защиты.

Если это условие не выполняется, выполняется код внутри ветви else. Эта ветвь должна передать управление, чтобы выйти из блока кода, в котором появляется этот оператор защиты. Он может сделать это с помощью оператора передачи управления, такого как return, break или continue, или он может вызвать функцию или метод, который не возвращает, например как fatalError ().

Дэн Болье
источник
7

Одним из преимуществ является устранение множества вложенных if letутверждений. Посмотрите видео WWDC "Что нового в Swift" около 15:30, раздел под названием "Пирамида Судьбы".

zaph
источник
6

Когда использовать охрану

Если у вас есть контроллер представления с несколькими элементами UITextField или каким-либо другим типом пользовательского ввода, вы сразу заметите, что вы должны развернуть необязательный textField.text, чтобы получить доступ к тексту внутри (если есть!). isEmpty здесь не поможет, без ввода текстовое поле просто вернет nil.

Таким образом, у вас есть несколько из них, которые вы разворачиваете и в конечном итоге передаете функции, которая отправляет их на конечную точку сервера. Мы не хотим, чтобы код сервера имел дело с нулевыми значениями или по ошибке отправлял недопустимые значения на сервер, поэтому сначала мы развернем эти входные значения с защитой.

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) {
  ...
}

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

// разделение интересов

Без охраны

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

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

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

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

Мед
источник
5

С помощью охраны наше намерение ясно. мы не хотим выполнять остальную часть кода, если это конкретное условие не выполняется. здесь мы можем расширить цепочку, пожалуйста, посмотрите на код ниже:

guard let value1 = number1, let value2 = number2 else { return }
 // do stuff here
Нарендра Г
источник
5

Охранное заявление собираюсь сделать. это пара разных

1) это позволяет мне уменьшить вложенный if оператор
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:

Охранное заявление

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

Synatx:

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. Swift 2: выход рано с охраной 2. Udacity 3. Заявление охранника

Ашок Р
источник
Но вы можете сделать последнее с заявлениями if? if condition { return }угрюмый?
Оливер Диксон
2

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

обратитесь к: http://ericcerney.com/swift-guard-statement/

Zgpeace
источник
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вступает в игру. Это дает вам более краткий способ выхода из раннего использования опций.

gunby
источник
1

Источник: Страж в Свифте

Давайте посмотрим на пример, чтобы понять это ясно

Пример 1:

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

В приведенном выше примере мы видим, что 3 больше 2, и оператор внутри предложения guard else пропускается, и выводится 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: "")

В приведенном выше примере мы используем guard let, чтобы развернуть опции. В функции getName мы определили переменную типа string myName, которая является необязательной. Затем мы используем guard let, чтобы проверить, равна ли переменная myName нулю или нет, если не присвоено имя и еще раз проверить, имя не пусто. Если оба условия удовлетворены, т.е. истинно, блок else будет пропущен и выведите «Условия выполнены с именем».

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

Здесь мы ничего не передаем функции, т. Е. Пустой строке, и, следовательно, условие false - это 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")

Здесь мы передаем «Hello» в функцию, и вы можете видеть, что вывод печатается «Условие выполнено Hello».

Адитья
источник