Подтвердить что ты не робот

Назначенные инициализаторы iOS: использование NS_DESIGNATED_INITIALIZER

У нас есть этот новый макрос, введенный в XCode 6: NS_DESIGNATED_INITIALIZER

Я искал в сети, но не мог найти хорошую документацию относительно того, как это использовать.

Синтаксически мы можем использовать его как:

- (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;

Но каковы возможные преимущества маркировки инициализатора этим макросом, а также какие вещи мы должны смотреть при использовании этого?

В основном меня интересуют случаи использования этого макроса. Любые ссылки/документация будут оценены.

4b9b3361

Ответ 1

Использование NS_DESIGNATED_INITIALIZER хорошо объяснено в http://useyourloaf.com/blog/2014/08/19/xcode-6-objective-c-modernization.html:

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

  • Назначенный инициализатор должен вызывать (через супер) назначенный инициализатор суперкласса. Где NSObject является суперклассом это просто [super init].
  • Любой инициализатор удобства должен вызвать другой инициализатор в классе, что в конечном итоге приводит к назначению инициализатор.
  • Класс с назначенными инициализаторами должен реализовать все назначенных инициализаторов суперкласса.

В качестве примера, если ваш интерфейс

@interface MyClass : NSObject
@property(copy, nonatomic) NSString *name;
-(instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;
-(instancetype)init;
@end

тогда компилятор проверяет, вызван ли (удобство) инициализатор init (назначенный) инициализатор initWithName:, поэтому это вызовет предупреждение:

-(instancetype)init
{
    self = [super init];
    return self;
}

и это будет нормально:

-(instancetype)init
{
    self = [self initWithName:@""];
    return self;
}

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

Например, этот подкласс Swift вызовет ошибку компилятора:

class SwClass: MyClass {
    var foo : String
    init(foo : String) {
        self.foo = foo
        super.init()
    }
}

и это будет нормально:

class SwClass: MyClass {
    var foo : String
    init(foo : String) {
        self.foo = foo
        super.init(name: "")
    }
}

Ответ 2

Самый распространенный способ сделать это:

@interface Person : NSObject

- (nullable instancetype)initWithName:(nonnull NSString *)name NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)init NS_UNAVAILABLE;

@property (nonatomic, nonnull) NSString *name;

@end

И реализация

@implementation Person

- (instancetype)initWithName:(NSString *)name
{
    self = [super init];
    if (self) {
        self.name = name;
    }
    return self;
}

@end

В этом случае вы не должны переопределять NS_DESIGNATED_INITIALIZER вашего метода суперкласса (NSObject init: в этом случае) - мы использовали NS_UNAVAILABLE, чтобы пометить этот метод как ненужный. Или вы можете переопределить его для вызова назначенного инициализатора с параметрами по умолчанию.

Ответ 3

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

Назначенный инициализатор не определяет, какой инициализатор вы должны использовать при создании объекта. Это объясняется в https://blog.twitter.com/2014/how-to-objective-c-initializer-patterns.