Я против "общего типа" 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,

  1. T.nibName: возвращает строку UIView
  2. nibName: возвращает строку MyCustomViewSubclass

Вы знаете, что здесь происходит? Зачем self а также T не один и тот же объект во время выполнения? Вот еще одна интересная вещь, которую я узнал. Вот что вы можете увидеть в отладчике, когда вы ставите точку останова в nibName геттер:

T.nibName: T.nibName nibName: 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]
    }
}

Но нет причин делать этот метод универсальным, и, как вы уже видели, он причиняет вам боль.

Другие вопросы по тегам