Принятие современного Objective C

За эти годы язык Objective C рос и развился. Несмотря на то, что базовые понятия и методы остаются тем же, части языка были через существенные изменения и улучшения. Эти модернизации улучшают безопасность типов, управление памятью, производительность и другие аспекты Objective C, упрощающего для Вас записать корректный код. Важно принять эти изменения в Вашем существующем и будущем коде, чтобы помочь ему стать более непротиворечивым, читаемым, и эластичным.

XCode обеспечивает инструмент, чтобы помочь внести некоторые из этих структурных изменений для Вас. Но перед использованием этого инструмента Вы хотите понять то, что изменяется, он предложит делать к Вашему коду, и почему. Этот документ выделяет некоторые старшие значащие и полезные модернизации для принятия в кодовой базе.

instancetype

Используйте instancetype ключевое слово как тип возврата методов, возвращающих экземпляр класса, к ним обращаются (или подкласс того класса). Эти методы включают alloc, init, и методы фабрики классов.

Используя instancetype вместо id в надлежащих местах улучшает безопасность типов в Вашем коде Objective C. Например, рассмотрите следующий код:

@interface MyObject : NSObject
+ (instancetype)factoryMethodA;
+ (id)factoryMethodB;
@end
 
@implementation MyObject
+ (instancetype)factoryMethodA { return [[[self class] alloc] init]; }
+ (id)factoryMethodB { return [[[self class] alloc] init]; }
@end
 
void doSomething() {
    NSUInteger x, y;
 
    x = [[MyObject factoryMethodA] count]; // Return type of +factoryMethodA is taken to be "MyObject *"
    y = [[MyObject factoryMethodB] count]; // Return type of +factoryMethodB is "id"
}

Из-за instancetype возвратите тип +factoryMethodA, тип того выражения сообщения MyObject *. С тех пор MyObject не имеет a -count метод, компилятор дает предупреждение о x строка:

main.m: ’MyObject’ may not respond to ‘count’

Однако из-за id возврат вводит +factoryMethodB, компилятор не может дать предупреждение о y строка. Начиная с объекта типа id может быть любой класс, и так как метод вызвал -count существует где-нибудь на некотором классе, к компилятору, из которого возможно что возвращаемое значение +factoryMethodB реализует метод.

Удостоверяться instancetype методы фабрики имеют правильное поведение разделения на подклассы, убедиться использовать [self class] при выделении класса вместо того, чтобы относиться непосредственно к имени класса. После этого соглашения гарантирует, что компилятор выведет типы подкласса правильно. Например, рассмотрите попытку сделать это с подклассом MyObject от предыдущего примера:

@interface MyObjectSubclass : MyObject
@end
 
void doSomethingElse() {
        NSString *aString = [MyObjectSubclass factoryMethodA];
}

Компилятор дает соблюдающее предупреждение об этом коде:

main.m: Incompatible pointer types initializing ’NSString *’ with an expression of type ’MyObjectSubclass *’

В примере, +factoryMethodA сообщение отправляет возвратам объект типа MyObjectSubclass, который является типом получателя. Компилятор соответственно решает что тип возврата +factoryMethodA должен иметь подкласс MyObjectSubclass, не суперкласса, в котором был объявлен метод фабрики.

Как принять

В Вашем коде замените случаи id как возвращаемое значение с instancetype где это необходимо. Это обычно имеет место для init методы и методы фабрики классов. Даже при том, что компилятор автоматически преобразовывает методы, начинающиеся с «выделения», «init», или «новый» и имеющие тип возврата id возвратиться instancetype, это не преобразовывает другие методы. Соглашение Objective C состоит в том, чтобы записать instancetype явно для всех методов.

Обратите внимание на то, что необходимо заменить id с instancetype для возвращаемых значений только, не в другом месте в Вашем коде. В отличие от этого id, instancetype ключевое слово может использоваться только в качестве типа результата в объявлении метода.

Например:

@interface MyObject
- (id)myFactoryMethod;
@end

должен стать:

@interface MyObject
- (instancetype)myFactoryMethod;
@end

Также можно использовать современный преобразователь Objective C в XCode для внесения этого изменения в код автоматически. Для получения дополнительной информации посмотрите Рефакторинг Вашего Кода Используя XCode.

Свойства

Свойство Objective-C является открытым или закрытым методом, объявленным с @property синтаксис.

@property (readonly, getter=isBlue) BOOL blue;

Свойства получают состояние объекта. Они отражают внутренние атрибуты объекта и отношения к другим объектам. Свойства обеспечивают безопасный, удобный способ взаимодействовать с этими атрибутами, не имея необходимость писать ряд пользовательских методов доступа (несмотря на то, что свойства действительно позволяют пользовательским методам get и методам set, при желании).

Используя свойства вместо переменных экземпляра в как можно большем количестве мест предоставляет много преимуществ:

Методы свойства следуют простому соглашению о присвоении имен. Метод get является именем свойства (например, date), и метод set является именем свойства с префиксом набора, записанным в Camel-регистре (например, setDate). Соглашение о присвоении имен для булево свойств состоит в том, чтобы объявить их с именованным методом get начиная со слова «,»:

@property (readonly, getter=isBlue) BOOL blue;

В результате вся следующая работа:

if (color.blue) { }
if (color.isBlue) { }
if ([color isBlue]) { }

При решении, что может быть свойством, имейте в виду, что следующее не является свойствами:

Кроме того, рассмотрите следующий ряд правил при идентификации потенциальных свойств в коде:

Как принять

Идентифицируйте ряд методов, квалифицирующих, чтобы быть преобразованными в свойство, такое как они:

- (NSColor *)backgroundColor;
- (void)setBackgroundColor:(NSColor *)color;

и объявите их использующий @property синтаксис с надлежащим ключевым словом (ами):

@property (copy) NSColor *backgroundColor;

Для получения информации о ключевых словах свойства и других соображениях, посмотрите Данные Инкапсуляции.

Также можно использовать современный преобразователь Objective C в XCode для внесения этого изменения в код автоматически. Для получения дополнительной информации посмотрите Рефакторинг Вашего Кода Используя XCode.

Макросы перечисления

NS_ENUM и NS_OPTIONS макросы обеспечивают краткий, простой способ определить перечисления и опции на языках на базе С. Они макросы улучшают завершение кода в XCode и явно указывают тип и размер Ваших перечислений и опций. Кроме того, этот синтаксис объявляет перечисления в пути, оцененном правильно более старыми компиляторами, и более новыми, которые могут интерпретировать базовую информацию о типе.

Используйте NS_ENUM макрос для определения перечислений, ряд значений, которые являются взаимоисключающими:

typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
        UITableViewCellStyleDefault,
        UITableViewCellStyleValue1,
        UITableViewCellStyleValue2,
        UITableViewCellStyleSubtitle
};

NS_ENUM макрос помогает определить и имя и тип перечисления, в этом случае названного UITableViewCellStyle из типа NSInteger. Тип для перечислений должен быть NSInteger.

Используйте NS_OPTIONS макрос для определения опций, ряд bitmasked значения, которые могут быть объединены вместе:

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
        UIViewAutoresizingNone                 = 0,
        UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
        UIViewAutoresizingFlexibleWidth        = 1 << 1,
        UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
        UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
        UIViewAutoresizingFlexibleHeight       = 1 << 4,
        UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

Как перечисления, NS_OPTIONS макрос определяет и имя и тип. Однако тип для опций должен обычно быть NSUInteger.

Как принять

Замените Ваш enum объявления, такие как этот:

enum {
        UITableViewCellStyleDefault,
        UITableViewCellStyleValue1,
        UITableViewCellStyleValue2,
        UITableViewCellStyleSubtitle
};
typedef NSInteger UITableViewCellStyle;

с NS_ENUM синтаксис:

typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
        UITableViewCellStyleDefault,
        UITableViewCellStyleValue1,
        UITableViewCellStyleValue2,
        UITableViewCellStyleSubtitle
};

Но когда Вы используете enum определить битовую маску, такой как здесь:

enum {
        UIViewAutoresizingNone                 = 0,
        UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
        UIViewAutoresizingFlexibleWidth        = 1 << 1,
        UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
        UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
        UIViewAutoresizingFlexibleHeight       = 1 << 4,
        UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
typedef NSUInteger UIViewAutoresizing;

используйте NS_OPTIONS макрос.

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
        UIViewAutoresizingNone                 = 0,
        UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
        UIViewAutoresizingFlexibleWidth        = 1 << 1,
        UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
        UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
        UIViewAutoresizingFlexibleHeight       = 1 << 4,
        UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

Также можно использовать современный преобразователь Objective C в XCode для внесения этого изменения в код автоматически. Для получения дополнительной информации посмотрите Рефакторинг Вашего Кода Используя XCode.

Объектная инициализация

В Objective C объектная инициализация основывается на понятии определяемого инициализатора, метод инициализатора, который ответственен за вызов одного из инициализаторов его суперкласса и затем инициализации его собственных переменных экземпляра. Инициализаторы, не определяющиеся инициализаторы, известны как инициализаторы удобства. Инициализаторы удобства обычно делегируют к другому инициализатору — в конечном счете завершению цепочки в определяемом инициализаторе — вместо того, чтобы выполнить инициализацию сами.

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

Для разъяснения различия между определяемыми и определяемыми ясными инициализаторами можно добавить NS_DESIGNATED_INITIALIZER макрос к любому методу в init семья, обозначая его определяемый инициализатор. Используя этот макрос представляет несколько ограничений:

Если какое-либо из этих ограничений нарушено, Вы получаете предупреждения от компилятора.

Если Вы используете NS_DESIGNATED_INITIALIZER макрос в Вашем классе, необходимо отметить все определяемые инициализаторы с этим макросом. Все другие инициализаторы будут считаться инициализаторами удобства.

Как принять

Идентифицируйте определяемые инициализаторы в своих классах и тегируйте их с NS_DESIGNATED_INITIALIZER макрос. Например:

- (instancetype)init NS_DESIGNATED_INITIALIZER;

Automatic Reference Counting (ARC)

Automatic Reference Counting (ARC) является функцией компилятора, обеспечивающей автоматическое управление памятью объектов Objective C. Вместо того, что вы имели необходимость помнить, когда использовать retain, release, и autorelease, ARC оценивает пожизненные требования Ваших объектов и автоматически вставляет надлежащее управление памятью, зовет Вас во время компиляции. Компилятор также генерирует надлежащий dealloc методы для Вас.

Как принять

XCode обеспечивает инструмент, автоматизирующий механические детали преобразования ARC (такие как удаление retain и release вызовы), и помогает Вам устранить проблемы, которые мигрант не может обработать автоматически. Для использования инструмента миграции ARC выберите Edit> Refactor> Convert to Objective-C ARC. Инструмент миграции преобразовывает все файлы в проекте использовать ARC.

Для получения дополнительной информации посмотрите Переход к Информации о версии ARC.

Рефакторинг кода Используя XCode

XCode обеспечивает современный преобразователь Objective C, который может помочь Вам во время процесса модернизации. Несмотря на то, что преобразователь помогает с механикой идентификации и применения потенциальных модернизаций, это не интерпретирует семантику Вашего кода. Например, это не обнаружит что Ваш -toggle метод является действием, влияющим на состояние Вашего объекта, и это ошибочно предложит модернизировать это действие, чтобы быть свойством. Удостоверьтесь, что вручную рассмотрели и подтвердили любые изменения, которые преобразователь предлагает вносить в Ваш код.

Из ранее описанных модернизаций преобразователь предлагает:
  • Изменение id к instancetype в надлежащих местах

  • Изменение enum к NS_ENUM или NS_OPTIONS

  • Обновление к @property синтаксис

Помимо этих модернизаций, этот преобразователь рекомендует дополнительные изменения в Вашем коде, включая:
  • Преобразование в литералы, таким образом, оператор как [NSNumber numberWithInt:3] становится @3.

  • Используя преобразование в нижний индекс, таким образом, оператор как [dictionary setObject:@3 forKey:key] становится dictionary[key] = @3.

Для использования современного преобразователя Objective C выберите Edit> Refactor> Convert to Modern Objective-C Syntax.