Моя напыщенная речь о том, почему я ненавижу Objective-c.

Эта статья не является сравнением Swift и Objective-C. В эти дни я работал над проектом, написанным на obj-c, и он напомнил мне, как я полюбил писать код в быстром темпе, и некоторые причины, по которым я ненавидел obj-c.

Когда я начинал свою карьеру в разработке для iOS, Swift 2.0 только что вышел. Компания, в которой я работал, рано приняла его, и я получил возможность использовать Swift в первые дни его существования. Но, в отличие от сегодняшнего дня, почти все учебники по iOS, которые вы можете найти, были написаны на obj-c. Все проекты компании, многие сторонние библиотеки и фреймворки были в obj-c, поэтому у меня не было другого выбора, кроме как изучать obj-c вместе с Swift. Хотя в то время я ненавидел это, изучение обоих языков оказалось стоящим вложением.

Затем Swift стал популярным, и многие компании начали его внедрять. Новые приложения писались очень быстро. Учебники были перенесены на Swift. Одна из причин, почему я считаю, что Swift стал популярным, заключается в том, что его легче освоить для новичков. Я помню, как боролся с синтаксисом obj-c, когда начинал.

// swift
let map = [String: Person]()
// obj-c
NSMutableDictionary<NSString *, Person *> *map = [NSMutableDictionary new];

Постепенно все больше и больше проектов, над которыми я работал, выполнялись в Swift, и я начал любить Swift больше, чем obj-c. Не поймите меня неправильно, obj-c - очень мощный язык, но в obj-c очень легко ошибиться, чем в swift.

Специальный переключатель:

Недавно я работал над проектом, написанным в объекте c, и он вернул мне воспоминания о том, почему я ненавидел obj-c. Поскольку в наши дни я пишу большую часть кода быстро, всякий раз, когда мне нужно писать код в obj-c, мой мозг должен постоянно переключать контекст между obj-c и swift. Много раз я обнаружил, что пытаюсь сделать что-то так, как я делаю быстро, например, вызов метода с использованием (точечной) нотации, с использованием String вместо NSString и т. Д.

// swift
article.publish()
// obj-c
[article publish];

Другой пример странного синтаксиса - оператор switch в obj-c.

switch(value) {
    case @"A":
        NSLog(@"This is medium article");
        break;
    case @"B": 
        Article *mediumArticle = [[Article alloc] init];
        break;
}

Как только вы это напишете, компилятор начнет выдавать вам ошибку в случае B. Почему? Потому что, если вы хотите создать переменную в случае переключателя, ее нужно заключить в {}. Я совершил именно эту ошибку и потерял один час, пытаясь понять, почему корпус переключателя не работает. Потом почувствовал смущение, осознав свою ошибку.

switch(value) {
    case @"A":
        NSLog(@"This is case 1");
        break;
    case @"B": {
        Article *mediumArticle = [[Article alloc] init];
        break;
    }
}
// This works YAY!! 🎉 

Также необходимо создать файл .h и .m для каждого класса, разные атрибуты для свойств, ivars, странную подпись метода, примитивные перечисления, отсутствие надлежащих модификаторов доступа, проблема с циклическим импортом - вот некоторые из вещей, которые мне не нравятся объективно -c.

Чтобы установить или не установить установить:

Как я сказал ранее, очень легко ошибиться в цели c. Позволь мне показать тебе.

Если вы объявите свойство с именем comment и метод с именем setComment, как показано ниже, это приведет к сбою вашего приложения при вызове этого метода.

@property (nonatomic) NSString *comment; 
- (void)setComment:(NSString *myComment) {
      self.comment = myComment;
}

Почему? Узнай сам 🧐

Подсказка: это как-то связано с тем, как работает свойство в obj-c.

Любопытный случай с obj-c nil:

Иногда то, как работает obj-c, может вызвать странную ошибку в вашем коде. Позвольте мне рассказать вам об одной из моих недавних ошибок.

У меня было объявлено некоторое перечисление и свойство этого типа перечисления.

typedef NS_ENUM(NSInteger, Mood) {
    MoodHappy,
    MoodSad
};
...
@property (nonatomic) EnumMood myMood;

Было установлено значение этого свойства: MoodSad. Где-то в моем коде мне пришлось удерживать слабую ссылку self. А затем получите доступ к этому перечислению, используя ссылку weakSelf.

__weak typeof(self) weakSelf;
switch (weakSelf.myMood) {
    case MoodHappy:
        NSLog(@"My mood is happy");
        break;
    case MoodSad:
        NSLog(@"My mood is sad");
        break;
}

Ошибки, ошибки, везде 🐛 🐛

Несмотря на то, что значение свойства myMood всегда было установлено равным MoodSad, код никогда не достигал значения MoodSad. Фактически, теперь, независимо от того, сколько случаев было для перечисления, код всегда выполнялся для первого случая в нашем перечислении, то есть MoodHappy.

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

// Object is nil
Article *mediumArticle;
// No Effect
[mediumArticle publish]; 

Если этот метод возвращает объект, то возвращаемое значение - 0 (т.е. ноль). В моем случае я допустил опечатку. Мой weakSelf был нулевым.

// Incorrect
__weak typeof(self) weakSelf;
// Correct 
__weak typeof(self) weakSelf = self;

Поэтому, когда я пытался получить доступ к значению перечисления myMood, он всегда возвращал 0 (ноль). Но в области перечислений obj-c значение для первого случая перечисления равно 0, второго - 1 и так далее.

В результате мой оператор switch всегда выполнял оператор для enum case со значением 0, то есть MoodHappy. Еще один час был потрачен на эту опечатку.

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

Надеюсь, вам понравилось читать.

Вы также можете прочитать эту статью прямо из моего блога по адресу https://nrlnishan.github.io/posts/objective-c-i-hate-you/