Начало Swift. Guard и defer

Довольно долго я не писал в блог. Случилось это потому, что я сменил основной язык программирования и полностью погрузился в мобильную разработку на нативном языке программирования от Apple Swift 2.0.

Все программирование для мобильных платформ ранее сводилось к Xamarin. Xamarin это удобно, это просто и быстро. Ранее я писал, что для быстрого старта следует выбирать Xamarin. Разумеется, тем кто знает C# еще проще войти в мир мобильной разработки. Но наступает момент, когда инструментов Xamarin и мощи Microsoft не хватает для действительно Rocket Science приложений. Тогда за дело берется Swift.

Swift это новый язык программирования для iOS / OS X от Apple. Ему идет второй год, недавно он стал Open Source и у него есть свой сайт swift.org. Переход с C# на Swift на удивление оказался не трудным. Во многих языках есть проблема проверки объектов на null. В C# и мире .net очень частым является исключение null reference exception. В iOS (Swift) применяется парадигма безопасного программирования. Это делает сам язык. Если в C# исключения используются очень часто для проверки типов, в конструкторах и в других всевозможных местах. Сразу встает вопрос где обрабатывать исключения, а если возникнет ошибка внутри обработки исключения и т. п. То в Swift работа с исключениями не является приоритетной. Вообще, методы которые посылают исключеиня нужно обязательно оборачивать в блок try. В Swift очень распространены Optional типы. Это аналог Nullable из C#. Это означает что объект может содержать значение (Some) или ничего (nil или None). 

guard

Рассмотрим один из популярных примеров. Во многих случаях нужно проверить объекты перед использованием. Например пользовательский ввод, данные из базы данных, что-то из файла и другое. Объекты могут содержать null (nil из iOS). Для этого в разных языках существуют разные проверки. В C# до последнего года приходилось городить огород из if (obj != null), год назад с выходом новой версии C# появился оператор .?, который выполняет следющий за ним выражение, если предыдущее не null. Это оказалось удобно. Что есть в Swift?

В Swift рекомендуется и используется конструкция if let. Например 

let a = "10".toInt()
let b = "5".toInt()
let c = "3".toInt()

if let a = a {
    if let b = b {
        if let c = c {
            if c != 0 {
                println("(a + b) / c = \((a + b) / c)")
            }
        }
    }
}

Можно заметить, что для трех переменных конструкция становится лесенкой. В народе это называется пирамида смерти. Возможное решение использование глобальных функций (аналог макросов), но это глупости, не решение а костыль. В Swift 1.2 появилась возможность проверять несколько Optional переменных за раз:

if let a = a, b = b, c = c where c != 0 {
    println("(a + b) / c = \((a + b) / c)")     // (a + b) / c = 5
}

Видно, что количество кода сократилось, повысился уровень восприятия. Часто приходится проверять условие, и, если условие не выполнилось возвращать управление. Ключевое слово guard проверяет условие, и если оно не выполнилось — требует выйти из текущего блока. Например:

guard let cellObj = cell as? MyCell else { return false; }
guard index == 0 else { return false; }
guard let userId = _userModel.Id else { return false; }

Таким образом осуществляется проверка нескольких условий, и выход из блока, если условия не выполнились. Основной (главный) блок функции будет выполнен, если все условия guard выполнились успешно. Рекомендуется использовать guard вместо if let потому что:

  1. cинтаксис такой, что код не захламляется условиями и скобками блоков
  2. безопаснее сделать все проверки сразу и выполнять код функции, если все данные есть и не nil
  3. дополнительная информация компилятору для выявления возможных ошибок

В своем коде мне очень понравилось использовать новый оператор.

defer

В то время как guard используется для проверки объектов на nil, чтобы избежать возможного исключения — defer призван отложить выполнения кода, объявленного в defer до окончания выполнения текущего блока. Звучит запутанно, попробуем разобраться на примере. Допустим вы открываете файл. Что нужно сделать обязательно? Правильно (надеюсь) — закрыть его. Но глупо закрывать файл, сразу после открытия. С другой стороны, если метод работы с файлом больше 20 строк то есть вероятность потеряться и не закрыть его. Swift гарантирует, что код будет запущено прежде, чем текущий блок будет закончен.

let file = openFile()
defer { closeFile(file) }

Открыли файл и обозначили, что его нужно закрыть. Далее работаем с файлом и не беспокоимся о том, что его нужно будет закрыть.

let hardwareStatus = fetchHardwareStatus()
guard hardwareStatus != "disaster" else { return }
file.write(hardwareStatus)

defer будет вызван независимо от того, какой из блоков guard сработает, даже если ни один из них не сработает. Если метод завершается по ошибке, return'ом или штатное завершение defer будет выполнен. 

Одна из интересных функций defer позволяет добавить в стек несколько блоков defer. Это означает, что все блоки будут выполнены позже (в обратном порядке FILO).

defer очень интересная особенность Swift, которая, уверен будет полезна и в других языках программирования. Возможно, к минусам можно отнести то, что из defer нельзя выполнить выход (return). В своих проектах мне еще не довелось полностью погрузиться идеологию defer, но это скоро исправится.

Комментарии

comments powered by Disqus