#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 не возвращают значение.
Поэтому, когда вы пишете:
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. Да, я понимаю это, я приму этот ответ как «отсутствующую функцию». Спасибо!