Пользователь
0,0
рейтинг
21 апреля 2015 в 17:44

Разработка → Опыт разработки приложения на Swift, наблюдения и выводы

Данная статья получилась из отчета программиста после «опыта» разработки небольшого приложения на новом языке Swift.

Дима занимается программированием почти два года и изучает языки самостоятельно. Документация, чужой код. На звание профессионала не претендует, но его опыт может быть полезен многим, кто подумывает начать кодить на Swift. Опыты, наблюдения и выводы. Приглашаем к дискуссии! Далее текст автора.

image



Введение


Когда я начал писать для IOS (а было это не так уж и давно), Objective-C меня немало смутил. Не то чтобы его было сложно понять, просто очень уж непривычно выглядел синтаксис. Со временем привык, конечно, но когда узнал о появлении Swift’а, при первой возможности, новый проект начал писать на нем.
Кроме того, есть моменты, о которые можно обжечься. Обо всем этом я и хотел порассуждать. Спорные моменты, замеченные мной в процессе работы, я попытался описать и обобщить.
К концу разработки и к концу написания статьи, я обновил Xcode, а с ним и Swift до версии 1.2. Выяснилось, что некоторые отмеченные мною проблемы потеряли актуальность. Тем не менее, я оставил их в тексте, поскольку они иллюстрируют выводы, которые актуальности не потеряли.

О совместимости


Первое существенно-слабое место — совместимость с pure C. Как мы все знаем, Swift полностью совместим с ObjC. ObjC полностью совместим с С. Совместим ли Swift с С? Тоже да. Специальные мостики создатели свифта сделали не только к ObjC, но и к C. А вот здесь раздел документации о том, как взаимодействовать с C API. Проблема у меня возникла при попытке использования Core Foundation (который как раз С-эшный API). CFNotificationCenterAddObserver принимает в качестве параметра указатель на С-функцию (с подобным, вероятно, есть и другие полезные функции из этой библиотеки). В Swift’е можно создать указатель на С-функцию (предусмотрен CFunctionPointer), но в самом swift-файле написать её нельзя, синтаксис-то у них разный! То есть для ObjC совместимость с С заключается в том, что вы в любом месте можете просто начать писать на С, соответственно, и использовать С-код из других мест (библиотеки), а для Swift’а возможно лишь использование библиотек или сторонних С, или ObjC файлов. Немного разная совместимость. Казалось бы, мелочь, но вот вам конкретная ситуация: если пишете только на Swift’е, Darwin notifications использовать не сможете.

Решение из интернета
Указатель на С-функцию в свифте — это CFunctionPointer. Теоретически, его можно создать из указателя на свифт-функцию, правда, мне не понятно как все это работает. Описание из интернета, как создать указатель на С-функцию в свифте, выглядит так:

The pointer to this function in C would have int (*)(void) type, while in Swift it will have CFunctionPointer<() -> Int32> type. To create CFunctionPointer, COpaquePointer is needed, e.g.:
let pointer = UnsafeMutablePointer<() -> Int32>.alloc(1)
pointer.initialize(getNextRandomValue)
let cPointer = COpaquePointer(pointer)
let functionPointer = CFunctionPointer<() -> Int32>(cPointer)
To call a function via a pointer to it, do the following:
let newCPointer = COpaquePointer(functionPointer)
let newPointer = UnsafeMutablePointer<() -> Int32>(newCPointer)
let rNumber = newPointer.memory()

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


Мое решение
Написал класс на ObjC. А когда понадобилось наследовать — наследников на свифте.


image

Перейдем к более общим вещам. Говоря о Swift «полностью совместим» [c ObjC], следует держать в уме, что все вещи из ObjC доступны в Swift не напрямую, а через мостики. Простейшие примеры: NSArray -> [AnyObject], NSDictionary -> [NSObject:AnyObject], а в строках свифта (String) вроде как есть функции из NSString, но опять же, не все (например, length), куча глобальных функций, начинающихся с objc_. И в большинстве случаев при переводе чего-либо из ObjC в Swift, мы видим AnyObject. Причины я знаю, но сейчас не о них, а о последствиях. Безусловно, AnyObject нужен, как возможность, ситуации разные бывают. Но его использование немного противоречит идеологии языка, ведь Swift характеризуется строгой типизацией (что должно повышать надежность кода). А при использовании любых фреймворков от Apple, мы везде в функциях видим AnyObject, ведь все они на ObjC. В результате, мы должны результат каждой второй функции из стандартной библиотеки кАстить к нужному типу. И не дай боже вам не угадать или перепутать какой должен быть тип! [Знаю, что можно сделать проверку с is, но вдруг куда-то улетучивается вся краткость и простота кода].

Итак, нам дают новый более надежный язык, за счет того, что мы обязаны указывать тип переменных. Но функции стандартных библиотек частенько возвращают не типизированные объекты. Конечно, условие «полной совместимости» не нарушено, но на этом моменте меня уже немного коробит.

Похожая ситуация, хотя и в более легкой форме, с опциональными переменными. Optional переменные вполне можно назвать инструментами языка, но если писать в том стиле, в котором предполагает свифт, то их приходится использовать очень нечасто. Функции же NS классов возвращают optional через одну. И это тоже заставляет менять стиль программирования в угоду совместимости.

[deprecated in 1.2]

Вернемся снова к частностям. Есть вещи в Foundation, которые пока совершенно недоступны в Swift. Например, класс NSDecimal. Хотя в документации было описание на свифте, использовать его было невозможно. Таким образом, если в функции ObjC был NSDecimal в качестве входного или выходного параметра, в свифте вы её просто не видели. А если писали — она не компилировалась из-за ошибки. Впрочем, если верить гуглу, кроме пользователей библиотеки Core Plot (из-за которой и я столкнулся с этой проблемой), этот класс не особо-то и был кому-то нужен. Правда, в 1.2 его теперь можно использовать. Но это то, с чем только я столкнулся. И наверняка, даже в комментариях найдутся и другие примеры недоступных классов.

Смотрите сами, но я, учитывая все вышесказанное, оценивая совместимость Swift и ObjC, скорее бы использовал слова «почти полная».
image

Об удобности


Мне не очень нравится Objective-C. И мне почти очень нравится Swift. Как утверждает заглавная страничка свифта от Apple, этот язык и инновационный, и современный. С этим я спорить не буду, но по моим ощущениям, он пока ещё не дорос до того состояния, когда им можно пользоваться безболезненно. Некоторые недостатки бросаются в глаза почти сразу при знакомстве с языком, но некоторые — достаточно неочевидны. Я же просто перечислю все то, что мешает сейчас, но я надеюсь, будет исправлено в будущем.

[deprecated in 1.2]

Первое, это затруднения при дебаге. В то время как словари и массивы в ObjC мы могли раскрывать и просматривать прямо в панели дебаггера, стандартные классы свифта раскрыть и посмотреть нельзя. Дебаггер только показывает их тип. Как я сказал, это касается стандартных классов, если вы используете NSDictionary или NSArray, они раскрываются, как и прежде (оно и понятно, Foundation же на ObjC). Как вариант, можно воспользоваться NSLog, но это очевидно менее удобно.

Аналогичная предыдущей ситуация с optional переменными. Какой бы ни был тип, в дебаггер вы увидите только nil эта переменная или some, и больше ничего от неё не добьётесь.

Fixed в 1.2. И some, и классы свифта теперь можно смотреть.

image

Не знаю, как назвать эту ситуацию, но происходит следующее: при анализе свифт-кода в Xcode что-то ломается, он выдает ошибку об этом и пропадает вся подсветка синтаксиса. Затем это что-то, видимо, перезапускается, подсветка опять появляется, и даже autocomplete снова работает. Но вот эти вот сбои происходят в тот момент, когда код некомпилируем (например, скобка открыта и пр.), а иногда, когда я ещё недописывал имя переменной или функции. Хоть Xcode не вылетает, и то хорошо, но тем не менее, все это не смотрится очень проработанно.

String — стандартный класс языка свифт. Имеет автоматический bridge к NSString. Однако функции length не имеет. Знаю, что есть тому причина, но факт остается фактом: такая простейшая функция как длина строки делается в Swift сложнее.

Варианты
Глобальная функция count (до совсем недавнего времени countElements). Только её нет в онлайн-документации (о чем позже), но вам может о ней кто-нибудь сказать. count(string)

Функция .utf16Count. Делает как раз то, что вы хотели бы сделать функцией .length, название выглядит почти также интуитивно. Пример: string.utf16Count

Убрали в 1.2. Xcode предлагает заменить на count(string.utf16).

endIndex — индекс последнего символа. Но является типом String.Index, чтобы получить Int придется помучаться. string.endIndex

каст в NSString и использовать .length: (string as NSString).length



Совсем частный случай. objc_setAssociatedObject последним параметром принимает параметр типа — typealias objc_AssociationPolicy = UInt. Рядом с типом определены переменные, определяющие эту политику OBJC_ASSOCIATION_ASSIGNб OBJC_ASSOCIATION_RETAINб OBJC_ASSOCIATION_COPY и пр. и они имеют тип Int. Почему? Зачем я должен явно приводить и писать в 2 раза длиннее: писать objc_AssociationPolicy(OBJC_ASSOCIATION_COPY)? Надеюсь, кто-нибудь объяснит (комментарии?!)

Об изменчивости


Swift — язык новый, даже с момента анонса ещё и года не прошло. Он активно развивается, что не может не радовать, и меняется, что немного заставляет задуматься. И порой, изменения могут существенно изменить стиль программирования, например, раньше константы необходимо было присваивать сразу при объявлении, а с версии 1.2 — можно после. Другие — это просто изменение синтаксиса (countElements -> count). Буквально во время написания этого текста обновил Xcode (напомню, версия 6.3 вышла 8.04.15 и содержит новую версию свифта 1.2), и вот проект некомпилируем. Почти все as просит заменить на as!, ну это из описания изменений новой версии видно. А вот неочевидное изменение: NSDictionary теперь почему-то не конвертируется автоматически в [NSObject:AnyObject], как и NSString в String, и NSSet -> Set, и NSArray -> [AnyObject] тоже больше нет, ну, вы поняли, как это работает. А раньше было. А теперь — нет. Есть наверняка тому веские причины, но я сейчас не о них говорю. Обратное присваивание, кстати, все ещё работает без явного приведения.

image

В Xcode есть функция для миграции на свежую версию свифта. По моему опыту обновления, увы, не работает как должно. Все несоответствия новой версии убрались только после третьего проведения этой операции (предыдущие разы тоже срабатывали, но убрались не всё), но и в результате код компилируемым так и не стал. В одном месте осталась неисправленной функция utf16Count, хотя до первого конвертирования Xcode предлагал автозамену, которую теперь пришлось заменять вручную. То есть мне пришлось 3 раза делать конвертирование, поскольку каждый раз он делал лишь часть замен, а потом ещё и править код в одном месте вручную. Боюсь представить, какие неудобства испытывают в больших проектах. Опасаясь подобной ситуации, я и не хотел обновлять Xcode до завершения проекта, хотя обновление уже вышло официально. Вот такая показательная ситуация с обновлениями.

И ещё совсем кратенько упомяну не задокументированные глобальные функции, которые, на мой взгляд, тоже являются признаком молодости языка: countElements, dropFirst, dropLast, first, last, prefix, suffix, reverse. Так что берите playground и пробуйте.

Заключение


Дебаг уже доработали, NSDecimal появился. Это из исправленных недостатков, в списке изменений 1.2 есть, конечно же, и другие улучшения. Видно, что работа над языком продолжает вестись активно. И я нисколько не сомневаюсь, что со временем и прочие недостатки исправят, и фишек добавят. Но есть «но». Foundation никто же не будет переписывать: ObjC же никуда не денется. А пока все базовые библиотеки написаны на ObjC, на Swift нельзя будет писать в том стиле, для какого он был спроектирован, поскольку всегда нужно будет взаимодействовать с ObjC. Что ж, надеюсь когда-нибудь увидеть новый фреймворк, созданный специально для свифта. Звучит фантастично, но если в планах Apple полный переход на свифт, то, полагаю, рано или поздно это случится. Ведь есть же Core Foundation, и есть Foundation.

Приглашаю к дискуссии.
Заменит ли Swift в ближайшем будущем ObjC

Проголосовало 659 человек. Воздержалось 256 человек.

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

Степан @RudkoDmitry
карма
9,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (24)

  • +5
    Мне лично кажется, что чтобы swift более менее взрослым стал, для него должен появиться открытый компилятор.
    • 0
      А что для этого нужно? Если какое то малейшее содействие apple, то я сомневаюсь, что такое будет.
  • +6
    Очень похоже на vendor locking. Вот наш девайс, для этого девайса вот наш сертифицированный язык, для этого языка вот наш сертицифированный статик-чека, вот наш сертифицированный… ну и тд. и тп. Открытые решения в наше время имеют больше шансов на успех, посмотрим что получится у Apple.
    • +3
      А много Вы пишете на ObjC вне OSX и IOS? а на C# вне .net (кроме Unity)?

      Мне правда интересно, т.к. нет знакомых программистов, чтобы спросить. Просто у меня сложилось впечатление, что хоть для этих языков и доступны компиляторы на других платформах, но используются они в основном на тех, которые их развивают.
      • +1
        Микрософт же наверное не просто так .net приоткрывает? Да и до этого же жил себе mono и им даже пользовались.
      • 0
        FYI: itmozg.ru/vacancy/show/123166#.VTdlHM4Vf8s
        Ядро платформы — видео-бэкенд — это кластер машин, оснащенных GPU, которые с помощью софта на Objective-C и Lisp’е создают и показывают видео.

        Это необычно, да.
  • 0
    Тоже намучился с указателями на функцию с аналогичным нестабильным результатом. Пришлось на просто си писать.
    А вообще свифту, самое главное, как вы сказали в конце, не хватает своей собственной стандартной библиотеки, где все будет адаптировано под возможности и концепции именно этого языка.
    • +2
      У swift и C разные call conventions, так что так лучше не делать. В будующем возможно добавят аналог extern «C».
      • 0
        про call conventions не знал, теперь понятно что указатель на С-функцию работать не должен. но в playground'е же сработало, это непонятно. хотя точно не помню, а код пока нет возможности посмотреть, может в pg я создавал функцию без параметров…
        • 0
          Для функции без параметров и не захватывающей переменные из внешнего контекста сработает, но без гарантий.
  • –7
    А такой вопрос, накидайте туториалов и уроков по Swift хороших, спасибо :)
    • 0
      На мой взгляд введение в свифт в документации достаточно полно написано. Там, где описания структур, вызовов функция и бриджей к objc, основы языка. Только его проштудировал перед началом работы с ним. Хотя, в целом, документация у apple не очень. Но, минимум половину необходимой информации из неё получить можно. Вторую половину можно извлечь из playground. Я поначалу не оценил эту фишку, но там достаточно удобно: поначалу на практике пробовать способности и ограничения языка, вместо того чтобы искать нужный пункт в документации. Затем тестировать куски кода до 100 строк без необходимости компилировать весь свой проект
      • 0
        Спасибо, просто я так давно ничего не писал, что уже сложно влиться в новый язык таким способом. Нужно все с нуля, как ребенку :)

        P.S. Те кто заминусовал меня — конкуренции боитесь? :)
        • +1
          выучить синтаксис Swift не так сложно… сложнее вникнуть в написание приложения. Можно в песочнице писать суровейшие алгоритмы, но если вы не знаете структуру написания аппликации — успеха не будет. Мне кажется надо брать туториалы именно по созданию приложений и повторять-повторять-повторять… Потом попробовать написать самому какой-то калькулятор со своим блекджеком.
          • 0
            Спасибо, у меня именно в этом и проблема, последний раз я писал давно и к мобильным никогда не прикасался.
            • +1
              Будет тяжело… надо сначала восстановить в памяти принципы ООП и для начала погружаться в освоение UIKit framework. Или может быть для вас будет более удобный вариант создавать саначал прототипы без логики и понять как работает Xcode. По этой теме есть неплохой курс Design + Code — гугл знает. Там именно по созданию работающих прототипов и как потом их «оживлять»
              • 0
                Да, как для детей, хороший вариант.
  • –4
    Название неудачное. Я бы перешел на язык с названием i (ай). Ой! Ай! А стриж — увольте. Я играю за буревестник.
    • +1
      не знаю зачем минусы: действительно ведь очень удачное название. Вполне в духе C, D, F и прочих однобуквенных языков. Ну и в духе продукции Apple, буква i в названии продукта символизировала бы что что этот язык лежит в основе его программного обеспечения.
  • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      С++, С#, Java, php. В основном диссонанс вызывает вызов фукций в виде [object function], это и писать менее удобно, хотя в xcode это достаточно хорошо реализовали. Именованные параметры при вызове функции тоже непривычно. И все вот эти вот директивы с @ в начале тяжело воспринимаются
      • +1
        Выражение [object function] не является вызовом функции. Это посылка сообщения function экземпляру какого-то объекта object.
  • 0
    Мигрировать на 1.2 мне помог автокомплит который помог подобрать новые параметры pattern matching:
    -  func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewFlowLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize! {
    +  func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    

    или
    -  func application(application: UIApplication!, willFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
    +  func application(application: UIApplication, willFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
    
  • 0
    Не встретил в статье про несовместимость enum и struct, написанных в obj-c и используемых в swift и наоборот.
    Ещё есть проблемы с глобальными переменными, #define.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.