Добавление сбойного необязательного инициализатора в Swift в расширении

#swift #initialization #swift3

#swift #инициализация #swift3

Вопрос:

Я пытаюсь добавить инициализатор с ошибкой удобства UIFont в качестве расширения. Итак UIFont , уже есть сбойный инициализатор:

 open class UIFont : NSObject, NSCopying {
    ...

    public init?(name fontName: String, size fontSize: CGFloat)
    ...
}
 

Итак, я хочу создать удобный инициализатор, который вызывал бы этот, что-то похожее на приведенный ниже код:

 public convenience init?(name fontName: String, size fontSize: CGFloat, weight fontWeight: String) {
    self.init(name: fontName   "-"   fontWeight, size: fontSize)
}
 

Пока все хорошо, это работает отлично. Теперь я хочу добавить некоторую пользовательскую логику, например:

 public convenience init?(name fontName: String, size fontSize: CGFloat, weight fontWeight: String) {
    if let font = self.init(name: fontName   "-"   fontWeight, size: fontSize) {
        self = font
    }
    else if let font = self.init(name: fontName, size: fontSize) {
        self = font
    }
    else {
        //
        // Add some failback code
        //
        return nil
    }
}
 

Здесь начинаются проблемы. То, что я хочу сделать, — это базовый удобный API, но мне не удается инициализировать шрифт условно. В верхнем примере в строке есть синтаксическая ошибка if let font = self.init , и она гласит: Initializer for conditional binding must have Optional type, not '()' . Но этот инициализатор на самом деле является необязательным, как определено UIFont class (см. Выше), поэтому я не вижу проблемы. Я попытался вернуть шрифт вместо его назначения self , но это не имеет значения.

Итак, мой вопрос: как правильно реализовать условную логику в сбойном инициализаторе?

Может быть, я делаю что-то не так, или это может быть просто отсутствующая функция / ошибка Swift?

Ответ №1:

Быстрая инициализация — одна из самых запутанных частей языка, особенно если вы используете Objective-C. Короткий ответ заключается в том, что:

В отличие от инициализаторов Objective-C, инициализаторы Swift не возвращают значение.

(из https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html).

Поэтому, когда вы пишете:

 public convenience init?(
    name fontName: String, size fontSize: CGFloat, weight fontWeight: String
) {
    let font = self.init(name: fontName   "-"   fontWeight, size: fontSize)
 

тип font is Void . Да, я знаю, это поднимает бровь, так как это другая модель, чем в ObjC. В Swift вы делегируете работу по инициализации другому инициализатору, но вы не управляете экземпляром напрямую. Таким образом, вы можете назвать это отсутствующей функцией.

Простое решение — создать фабричный метод вместо инициализатора:

 public static func from(
    name fontName: String, size fontSize: CGFloat, weight fontWeight: String
) -> UIFont? {
    if let font = self.init(name: fontName   "-"   fontWeight, size: fontSize) {
        return font
    }
    else if let font = self.init(name: fontName, size: fontSize) {
        return font
    }
    else {
        //
        // Add some failback code
        //
        return nil
    }
}
 

Комментарии:

1. Я знаю о фабричном методе как об обходном пути (именно так я сейчас с ним справляюсь), но я искал лучший API для инициализации. Но, согласно этому сообщению в блоге, developer.apple.com/swift/blog/?id=17 Я надеялся, что там что-то есть.

2. Я понимаю, что вы имеете в виду. Разница в том, что в сообщении в блоге показана инициализация типов значений, которые следуют семантике копирования. Для типов значений self могут быть изменены. Это относится как к инициализатору, так и к любому другому методу, помеченному как mutating . Хотя у меня нет отличной ментальной модели для этого, пожалуйста, обратитесь к developer.apple.com/library/content/documentation/Swift /… для получения более подробной информации о различиях между ссылочным типом и делегированием инициализации типа значения.

3. Да, я понимаю это, я приму этот ответ как «отсутствующую функцию». Спасибо!