Вызов метода экземпляра в назначенном инициализаторе класса Swift

В языке программирования Swift обязательно, чтобы

«Назначенный инициализатор должен убедиться, что все свойства, введенные его классом, инициализированы, прежде чем он делегирует инициализатору суперкласса». источник цитаты из другого s/o

В противном случае будет отображаться ошибка, подобная следующей

Свойство self.baz не инициализируется при вызове super.init

Я хотел бы реализовать следующий код Objective-C в Swift:

@interface FooBar : NSObject

@property (nonatomic, readonly) NSString *baz;

@end

@implementataion FooBar

@synthesize baz = _baz;

- (instancetype)initWithString:(NSString *)string {
    self = [super init];
    if (self) {
        _baz = [self parseString:string];
    }
    return self;
}

- (NSString *)parseString:(NSString *)string {
    // Perform something on the original string
    return ...;
}

@end

Следующая реализация выдала бы ошибку компилятора, как описано выше:

class FooBar: NSObject {
    let baz: NSString?

    init(string: NSString?) {
        super.init()
        baz = parseString(string)
    }

    private func parseString(string: NSString?) -> NSString? {
        // Perform something on the original string
        return ...;
    }
}

И если бы я сделал следующее, я бы получил сообщение об ошибке

Использование self в вызове метода 'parseString' до того, как super.init инициализирует себя

class FooBar: NSObject {
    let baz: NSString?

    init(string: NSString?) {
        baz = parseString(string)
        super.init()
    }

    private func parseString(string: NSString?) -> NSString? {
        // Perform something on the original string
        return string;
    }
}

Итак, мое решение в настоящее время заключается в использовании метода частного класса:

class FooBar: NSObject {
    let baz: NSString?

    init(string: NSString?) {
        baz = FooBar.parseString(string)
        super.init()
    }

    private class func parseString(string: NSString?) -> NSString? {
        // Perform something on the original string
        return ...;
    }
}

Мой вопрос в том, есть ли лучший способ добиться того же или метод частного класса - лучший подход?


person Mark Kang    schedule 27.12.2015    source источник


Ответы (1)


Как вы правильно упомянули, компилятор жалуется на "Использование self в вызове метода 'parseString' до того, как super.init инициализирует себя". Причина этого в том, что компилятор должен гарантировать, что вы не получите доступ к чему-либо на self до того, как он завершит инициализацию. Поскольку компилятор не хочет (или даже не может) проверять каждый исходящий вызов метода до завершения инициализации, он просто запрещает любое использование self до его завершения.

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

Вы можете просто сделать его функцией static или class, как вы уже сделали, и это прекрасно.

Но я бы пошел еще дальше и полностью убрал его из FooBar. Для этого есть несколько вариантов:

  • создайте класс Helper, где вы также определите его как class function
  • создайте класс Helper, где вы определите его как instance function, и создайте экземпляр Helper в своем классе FooBar или создайте глобальный и передайте его также в инициализации.
person luk2302    schedule 27.12.2015
comment
Чтобы сохранить больше духа исходного кода, вы можете просто инициализировать baz в объявлении как nil. В конце концов, это то, что происходит на стороне Objective-C. Это позволит избежать первоначальной ошибки, которую вы получили. - person Avi; 27.12.2015
comment
@Avi, но это лишило бы его константы. - person luk2302; 27.12.2015
comment
Как преобразование parseString в метод закрытого класса оставляет baz константой? Дело в том, что он присваивается после объявления в любом случае. Это не константа. Кроме того, в оригинальном Objective-C это тоже не константа. - person Avi; 27.12.2015
comment
@Avi: вы можете инициализировать константы после их объявления в Swift. - person Crowman; 27.12.2015
comment
@Avi, пожалуйста, посмотрите на код OP, опубликованный в его вопросе, baz по-прежнему остается константой. Единственный раз, когда вам разрешено что-то присваивать, это конструктор. - person luk2302; 27.12.2015
comment
@ luk2302: имеет смысл. И спасибо за предложение о полном удалении. Обычно я предпочитаю первый вариант второму, поскольку я предпочел бы внедрить вспомогательный класс в FooBar, а не создавать экземпляр изнутри. Однако первый вариант отделяет FooBar от класса Helper. - person Mark Kang; 30.12.2015