Статья основана на Xcode 7.3 и соответствующих ему языковых версиях
WTF? Именно таким вопросом вы могли задаться, читая предыдущую статью из этой серии. И правда, полезной информации там не так много, но а что вы хотели? Она же вводная =) Итак, давайте не будем отвлекаться, а нырнем сразу в пучину сравнений Swift и Objective-C.
Swift X Objective-C: Interface Declaration
Сейчас вашему взору предстанет достаточно интересная особенность Swift, которую можно смело называть эталонной, если вопрос касается сравнения объема кода. Дело в том, что в Swift нет обязательного объявления интерфейса. Многие из вас, возможно, впервые услышали об интерфейсе. Да, мои юные друзья, было дело в Objective-C. В Swift даже случайно можно нарваться на него, но чаще всего это скрыто от наших глаз. В Objective-C все классы делятся на .h и .m файлы. Так вот — интерфейс это .h файл. В нем мы объявляем все свойства и методы для нашего класса. Реализуем же их в .m файле. Давайте посмотрим, как это выглядит:
.h — файл интерфейса:
1 2 3 4 | #import <Foundation.h> @interface MyClass: NSObject - (id) initWithA: (int)a andB: (NSString *)b; @end |
.m — файл реализации:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #import "MyClass.h" @interface MyClass() @property (assign, nonatomic) int a; @property (strong, nonatomic) NSString *b; @end @implementation MyClass @synthesize a; @synthesize b; - (id) initWithA: (int)a andB: (NSString *)b { self = [super init]; if (self) { _a = a; _b = b; } return self; } @end |
создание объекта от нашего класса:
1 | MyClass *mc = [[MyClass alloc] initWithA: 2 andB @"string"]; |
В Swift это все перекочевало в единый .swift файл:
1 2 3 4 5 6 7 8 | class MyClass { var a: Int var b: String init(a: Int, b: String) { self.a = a self.b = b } } |
Да, именно! Этот кусочек кода — аналог того кода, что выше. А создание объекта этого класса выглядит так:
1 | var mc = MyClass(a: 2, b: "string") |
Это показывает, насколько сильно Swift упрощает структуру кода, делая ее менее громоздкой и более понятной.
Swift X Objective-C: Syntax Declaration
Теперь отпала необходимость в переменных экземпляра. Да, Objective-C научился создавать их автоматически. Однако в Swift к вопросу подошли в корне иначе. Давайте посмотрим на Swift код:
1 2 3 4 5 6 7 8 | class MyClass { let a: Int // Сейчас "a" неизменяема var b: String init(a: Int, b: String) { self.a = a // И мы инициализировали ее ПОСЛЕ объявления self.b = b } } |
Теперь, давайте создадим новый экземпляр MyClass:
1 | var mc = MyClass(a: 2, b: "string") |
и попытаемся переназначить значение «a», которое мы объявили константой:
1 | mc.a = 3 //error: Cannot assign 'a' to mc |
с «b» проблем не возникнет, так как мы объявили его переменной:
1 | mс.b = "another string" |
Если говорить категорически просто — var и let — необходимый минимум для счастья.
Давайте теперь посмотрим, как это работает в Objective-C. Неизменяемые значения наверняка работают не так. Если объявить некую константу, она обязательно должна быть статичной и инициализироваться немедленно. Для того, что бы сделать нечто, похожее по функционалу на код выше, вам необходимо будет создать «сеттер» для переменной, а константу защитить от переназначения. Руками.
Посмотрим пример:
.h:
1 2 3 4 5 | #import <Foundation.h> @interface MyClass: NSObject - (id) initWithA: (int)a andB: (NSString *)b; - (void) anotherMethod; @end |
.m:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #import "MyClass.h" @interface MyClass() @property (assign, nonatomic) int a; @property (strong, nonatomic) NSString *b; @end @implementation MyClass @synthesize a; @synthesize b; - (id) initWithA: (int)a andB: (NSString *)b { self = [super init]; if (self) { _a = a; _b = b; } return self; } //Мы должны переопределить сеттер, что бы защитить константу от переназначения. - (void) setA:(int)a { NSAssert(false, "You are not allowed to assign to `a`"); } //Этот метод изменяет "a", показывая, что на самом деле оно неизменно - (void) anotherMethod { _a = 25; } @end |
Создаем объект от класса:
1 2 | MyClass *mc = [[MyClass alloc] initWithA: 2 andB @"string"]; [mc anotherMethod]; //После того, как мы вызываем этот метод, "a" изменяется, хотя неизменна. |
Это показывает, что мы можем сделать что либо константой для внешнего обращения, но сам класс вполне способен менять свои значения внутри себя. Хотя лучше так не делать. В Objective-C константы должны оставаться статичными:
1 2 3 4 5 6 | @interface MyClass() { const int _myConst = 42; } @property (assign, nonatomic) int a; @property (strong, nonatomic) NSString *b; @end |
На этом мы, пожалуй, закончим. Как вы уже могли обратить внимание — Swift действительно превосходит Objective-C по простоте и легкости. И чем дальше мы будем заходить, тем больше отличий вы заметите. Однако, обращайте внимания и на сходства! Это очень важный опыт, который сильно упростит вам работу с документацией. Когда вы увидите эту тонкую нить сходства — вы сможете использовать документацию/уроки для Objective-C и программировать при этом на Swift. И наоборот.
Все «прелести» Swift заканчиваются тогда, когда пытаешься кодить через нормальную IDE (AppCode)…
Я уже молчу, про то, что если вместо ! поставить ?, то компилятор виснет на процессе компилинга, навсегда, сжирая весь ЦПУ.
Имхо, swift сыроват…
«Я уже молчу, про то, что если вместо ! поставить ?, то компилятор виснет на процессе компилинга» — это свидетельствует о сырости Xcode, не языка =) И у меня таких проблем на данный момент не замечено
//Мы должны переопределить сеттер, что бы защитить константу от переназначения.
А как же readolny?
@synthesize a;
@synthesize b;
А зачем это писать если это делается автоматически?
Тут показан более длинный демонстрационный путь. Наглядность повысится