Статьи

Swift 2: выход рано с охраной

Вас удивляет, что я посвящаю учебник простому дополнению в качестве guard заявления? Я надеюсь, вы лучше поймете мое волнение в конце этого урока. В этом уроке я надеюсь убедить вас, что guard — это не что иное, как избыточное дополнение к языку программирования Swift.

Условные выражения являются фундаментальным компонентом каждого языка программирования. Objective-C и Swift не являются исключением из этого правила. Если вы планируете написать приложение любой сложности, условные обозначения будут пересекаться на вашем пути, их избежать не избежать.

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

Чтобы сводить вложенные операторы if к минимуму, я часто использую следующий шаблон в Objective-C.

1
2
3
4
5
6
7
— (void)fetchListOfCustomers:(NSArray *)customers {
    if (!self.reachable) return;
    if (!self.connected) return;
    if (!customers || ![customers count]) return;
 
    …
}

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

Приведенный выше пример переводится в следующий, немного более сложный эквивалент.

1
2
3
4
5
6
7
— (void)fetchListOfCustomers:(NSArray *)customers {
    if (self.reachable && self.connected) {
        if (customers && [customers count]) {
            …
        }
    }
}

Вы видите проблему, скрывающуюся в этом примере? Мы уже вложили два уровня в глубину, не сделав ничего интересного.

Легко перевести вышеуказанный шаблон в Swift. Синтаксис выглядит аналогично, но поскольку customers являются необязательными, нам нужно развернуть аргумент customers прежде чем мы сможем получить доступ к его значению.

1
2
3
4
5
6
7
func fetchListOfCustomers(customers: [Customer]?) {
    if !reachable { return }
    if !connected { return }
    if let customers = customers where customers.count > 0 {
        print(customers)
    }
}

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

1
2
3
4
5
6
7
func fetchListOfCustomers(customers: [Customer]?) {
    guard reachable else { return }
    guard connected else { return }
    guard let customers = customers where customers.count > 0 else { return }
 
    print(customers)
}

Есть несколько вещей, которые стоит отметить. Давайте начнем с синтаксиса.

Ключевое слово guard подчеркивает, что мы проверяем требование. Мы защищаемся от чего-то. В этом примере мы явно проверяем, reachable ли и connected они. Если это не так, то мы покидаем метод рано. Дело в том, что синтаксис более точен в отношении требований, чем обычный оператор if .

Обратите внимание, что у оператора guard всегда есть предложение else . Предложение else выполняется, если условие оператора guard оценивается как false . Использование guard имеет больше смысла, когда вы проверяете требования.

В предложении else вы должны перенести управление за пределы области действия, в которой появляется оператор guard . Мы используем оператор return в приведенном выше примере, но вы можете, например, использовать оператор continue если вы находитесь в цикле или генерируете ошибку. Взгляните на обновленный пример ниже, в котором мы выкидываем ошибку в предложении else . Обратите внимание на ключевое слово throws в объявлении метода, которое указывает, что fetchListOfCustomers(_:) является методом throwing.

1
2
3
4
5
6
7
func fetchListOfCustomers(customers: [Customer]?) throws {
    guard reachable else { throw APIError.APIErrorUnreachable }
    guard connected else { throw APIError.APIErrorNotConnected }
    guard let customers = customers where customers.count > 0 else { throw APIError.APIErrorNoCustomers }
     
    …
}

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

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

Если вы думали, что guard — это простая вариация заявления Свифта, то, надеюсь, я убедил вас в обратном. Хотя в большинстве ситуаций заявления по-прежнему будут вашим предпочтительным инструментом, в определенных ситуациях у guard есть ряд преимуществ. Это особенно верно, если используется в сочетании с обработкой ошибок, которая также была представлена ​​в Swift 2.