Недавно я разрабатывал несколько сильно ориентированных на протокол приложений фреймворков с Swift и заметил несколько (по-видимому) нечетное поведение со статическими функциями в расширениях протокола, особенно там, где функции расширения вызываются из метатипов.
То, как я изначально обнаружил это поведение, заключалось в устранении ошибки, в которой тип объекта был изменен, по-видимому, невозможным способом. Я проследил проблему и в конце концов определил, что это связано с тем, что в статической функции Self
и Self
могут потенциально содержать разные типы (примечание: я взял называть эти "Big S Self" и "Little s self", соответственно). Я продемонстрирую это с помощью примера с голыми костями из того, что я взбивал на игровой площадке:
class SomeBaseClass: SomeProtocol {}
class SomeChildClass: SomeBaseClass {}
protocol SomeProtocol {}
extension SomeProtocol {
static private func getName() -> String {
return "\(self): \(type(of: self))"
}
static func ambiguousName() -> String {
return getName()
}
static func littleName() -> String {
return self.getName()
}
static func bigName() -> String {
return Self.getName()
}
}
let child: SomeBaseClass.Type = SomeChildClass.self // SomeChildClass.Type
print(child.ambiguousName()) // "SomeChildClass: SomeBaseClass.Type\n"
print(child.littleName()) // "SomeChildClass: SomeBaseClass.Type\n"
print(child.bigName()) // "SomeBaseClass: SomeBaseClass.Type\n"
print(SomeChildClass.ambiguousName()) // "SomeChildClass: SomeChildClass.Type\n"
print(SomeChildClass.littleName()) // "SomeChildClass: SomeChildClass.Type\n"
print(SomeChildClass.bigName()) // "SomeChildClass: SomeChildClass.Type\n"
print(SomeBaseClass.ambiguousName()) // "SomeBaseClass: SomeBaseClass.Type\n"
print(SomeBaseClass.littleName()) // "SomeBaseClass: SomeBaseClass.Type\n"
print(SomeBaseClass.bigName()) // "SomeBaseClass: SomeBaseClass.Type\n"
Можно видеть, что когда статические функции вызывается из метатипа, результат может отличаться, если этот метатип присваивается переменной с объявленным типом метатипа родительского класса.
Мой вопрос в том, как Self
знать, какой тип он есть? Как тогда Self
знает, какой тип он есть? Для меня не имело смысла, почему Self
был даже доступен в статической функции в любом случае, поскольку в действительности нет экземпляра. Я бы подумал, что нужно использовать Self
исключительно, но теперь я думаю, что это не так, поскольку Self
и Self
доказали, что в некоторых сценариях возникают разные результаты.
Кроме того, существует ли какая-либо причина, по которой тип Self
используется, когда опускаются либо Self
или Self
, как в операторе return return getName()
в функции ambiguousName()
?
Для меня, я думаю, самая странная часть - это когда type(of: self)
возвращает SomeBaseClass.Type
при вызове из вызова функции child.littleName()
. Не должен ли "динамический тип" быть SomeChildClass
?