Я против "общего типа" T разницы при прекращении расширения
Я сталкиваюсь с интересным поведением, которое я не понимаю. Вот код, который производит это поведение:
import UIKit
protocol UIViewNibLoading {
static var nibName: String { get }
}
extension UIView : UIViewNibLoading {
static var nibName: String {
return String(describing: self)
}
}
extension UIViewNibLoading where Self : UIView {
static func loadFromNib<T: UIViewNibLoading>() -> T {
print(T.nibName)
print(nibName)
return UINib(nibName: nibName, bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! T
// CRASH: return UINib(nibName: T.nibName, bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! T
}
}
Вот вывод из консоли, когда этот код выполняется:
UIView
MyCustomViewSubclass
Когда я позвоню тогда loadFromNib
метод моего пользовательского класса. Он производит два разных поведения в зависимости от того, как я могу получить nibName
,
- T.nibName: возвращает строку
UIView
- nibName: возвращает строку
MyCustomViewSubclass
Вы знаете, что здесь происходит? Зачем self
а также T
не один и тот же объект во время выполнения? Вот еще одна интересная вещь, которую я узнал. Вот что вы можете увидеть в отладчике, когда вы ставите точку останова в nibName
геттер:
Это называется:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == WidgetAddTableViewController.SectionIndexRecent {
return WidgetAddHeaderView.loadFromNib()
} else if section == WidgetAddTableViewController.SectionIndexFreeAndPremium {
return WidgetAddFilterHeaderView.loadFromNib()
}
return nil
}
Спасибо за любое объяснение.
1 ответ
self
разрешается во время выполнения. T
разрешается во время компиляции. Поэтому во время компиляции ваш код ведет себя так:
let returnValue: UIView? = WidgetAddHeaderView.loadFromNib()
return returnValue
loadFromNib
является универсальным по своему типу возврата. Учитывая этот код, единственный допустимый тип возвращаемого значения UIView
, Опять же, это решается во время компиляции.
self
с другой стороны, это просто переменная. Это очень специфическая переменная, но на самом деле это просто переменная. Имеет значение во время выполнения. Так type(of: self)
оценивается во время выполнения. А динамическая отправка обрабатывается во время выполнения.
Ошибка в том, что вы на самом деле не хотите возвращать "некоторый неизвестный T, который соответствует UIViewNibLoading" (то, что вы говорите, вы возвращаете, делая тип возвращаемого значения универсальным). Что вы имеете в виду, чтобы вернуться Self
, класс, членом которого является статическая функция (определяется во время компиляции). Так ты говоришь так:
extension UIViewNibLoading where Self : UIView {
static func loadFromNib() -> Self {
print(nibName)
return UINib(nibName: nibName, bundle: nil)
.instantiate(withOwner: nil, options: nil)[0] as! Self
}
}
Или вы могли бы обещать меньше (так как вашему звонящему на самом деле все равно) и сделать это:
extension UIViewNibLoading where Self : UIView {
static func loadFromNib() -> UIView {
print(nibName)
return UINib(nibName: nibName, bundle: nil)
.instantiate(withOwner: nil, options: nil)[0]
}
}
Но нет причин делать этот метод универсальным, и, как вы уже видели, он причиняет вам боль.