Я против "общего типа" T разницы при прекращении расширения - PullRequest
0 голосов
/ 24 мая 2018

Я сталкиваюсь с интересным поведением, которое я не понимаю.Вот код, который вызывает это поведение:

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 getter:

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 Ответ

0 голосов
/ 24 мая 2018

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]
    }
}

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

...