Ответ 1
Вот мое понимание того, что вы видите:
-
Вы получаете ошибку компиляции
Method 'loadFromNib' in non-final class 'UIView' must return 'Self' to conform to protocol 'NibLoadable'
в точке, где вы объявляетеextension UIView: NibLoadable {}
. Давайте посмотрим, что это означает для компилятора. Он говорит: "UIView (и все его подклассы, поскольку это не конечный класс), принимают протокол NibLoadable. Это означает, что для UIView существует метод с сигнатуройstatic func loadFromNib(name: String?) -> UIView
, потому чтоSelf
в этом контекстеUIView
."Но что это значит для подклассов UIView? Они наследуют их соответствие и могут наследовать реализацию метода из самого UIView. Таким образом, любой подкласс UIView может иметь метод с сигнатурой
static func loadFromNib(name: String? = nil) -> UIView
. Однако протоколNibLoadable
, который также соответствует всем подклассам, указывает, что возвращаемый тип этого метода должен бытьSelf
. А в случае любого подкласса UIView (например, пусть говорят "MyView" ), возвращаемый тип унаследованного метода будетUIView
, а неMyView
. Поэтому любой подкласс будет нарушать контракт протокола. Я понимаю, что расширение вашего протокола используетSelf
и не создало бы эту проблему, но технически вы все равно могли бы реализовать метод непосредственно в расширении UIView, и похоже, что компилятор Swift просто не позволит его вообще эта причина. Более эффективная реализация может найти компилятор Swift, подтверждающий существование расширений протокола, который обеспечивает реализацию, и не существует противоречивой унаследованной реализации, но в настоящее время это просто не существует. Поэтому для безопасности я предполагаю, что компилятор запрещает ЛЮБЫЕ протоколы, которые имеют методы сSelf
типами возврата, которые принимаются нечетным классом. Таким образом, вы видите ошибку.Однако, делая UIView окончательным классом, это полное наследование метода несоответствующего метода и проблема исчезнут, что исправляет ошибку.
-
Причина, по которой изменение типа возвращаемого значения в протоколе для UIView устраняет все, заключается в том, что не имеет "Я", поскольку возвращаемый тип теперь уменьшает озабоченность компилятора о наследуемых версиях метода, имеющего несоответствующий тип возврата. Например, если UIView должен был реализовать метод
static func loadFromNib(name: String?) -> UIView
, а подклассы, унаследованные этим методом, контракт по контракту будет сохраняться для этих подклассов, поэтому нет проблем!С другой стороны, вывод типа работает, поскольку подклассы UIView получают реализацию своего метода из расширения протокола (поскольку метод не реализован непосредственно в UIView). Эта реализация возвращает тип
Self
, который сообщает компилятору, что возвращаемое значение имеет тот же тип, что и тип, в котором был вызван метод, и протокол удовлетворен, поскольку любой подкласс UIView будет иметь типSelf
, который подкласс требуемого типаUIView
. -
Удаление предложения where работает только в этом конкретном случае, потому что вы изменили метод протокола для возврата UIView, а расширение протокола определяет реализацию метода соответствия, которая возвращает
Self
, а затем только UIView получает это расширение в ваш образец кода. Таким образом, требование протокола метода, возвращающегоUIView
, соответствует реализации UIView, которая возвращаетSelf
(которая в данном случае оказываетсяUIView
). Но, если вы попытаетесь сделать любой тип, отличный от UIView, получить способ расширения протокола, например.class SomeClass : NibLoadable {}
или даже
class MyView:UIView, NibLoadable {}
компилятор не допустит этого, потому что тип возвращаемого типа
Self
в методе расширения протокола не будет соответствоватьUIView
, требуемому в протоколе. Мне кажется, что в случае "MyView" или других подклассов UIView ошибка компилятора может быть ошибкой, поскольку метод, возвращающийMyView
, удовлетворяет требованию протокола, чтобы метод возвращалUIView
, если MyView наследовал из UIView.
Подводя итог некоторым ключевым моментам:
-
Не похоже, что расширение протокола имеет какую-либо роль в ошибке компилятора, которую вы отметили. Только это также создаст ошибку:
protocol NibLoadable { static func loadFromNib(name: String?) -> Self } extension UIView: NibLoadable {}
Итак, похоже, что компилятор не позволяет нефинальным классам принимать протоколы, используя стандартные реализации методов, которые имеют тип возврата Self, period.
-
Если вы меняете сигнатуру метода протокола, возвращаете
UIView
вместоSelf
, что предупреждение конкретного компилятора исчезает, потому что больше нет возможности подклассов, наследующих тип возврата суперкласса и нарушения протокола. Затем вы можете добавить соответствие протоколу UIView с расширением вашего протокола. Однако вы получите другую ошибку, если попытаетесь принять протокол для любого типа, кроме UIView, потому что тип возвращаемого протоколаUIView
не будет соответствовать типу возврата метода расширенияSelf
, за исключением одного случая UIView. Это может быть ошибкой, на мой взгляд, потому чтоSelf
для любого подклассаUIView
должен соответствовать требуемому контракту типаUIView
. -
Но как ни странно, если вы примете протокол только в UIView, подклассы UIView наследуют их соответствие протоколу (избегая запуска любой из двух вышеперечисленных ошибок компилятора) и получая их общие реализации из протокола до тех пор, пока UIView явно не реализует сам протокол. Таким образом, подклассы получат вывод типа соответствующего
Self
и удовлетворяют контракту протокола для того, чтобы этот метод возвращалUIView
.
Я уверен, что во всем этом есть несколько ошибок, но кто-то из команды Swift должен подтвердить это, чтобы быть уверенным.
ОБНОВЛЕНИЕ
Некоторые пояснения от команды Swift в этой теме Twitter:
https://twitter.com/_danielhall/status/737782965116141568
Как и предполагалось, это ограничение компилятора (хотя, по-видимому, не считается прямой ошибкой), что согласование протокола не рассматривает подтипы, только точные совпадения типа. Поэтому extension UIView:NibLoadable {}
будет работать, когда метод протокола определяет тип возврата UIView
, но extension MyView:NibLoadable {}
не будет.