Мне любопытно, как проверка типов работает в Swift, я немного поигрался и создал класс Living и класс Animal, который наследуется от класса Living. Класс Cat наследуется от класса Animal и класса Dog, у которого нет родительского класса.
Моя иерархия классов на данный момент:
class Living {
let heartRatePerMin: Int
init(heartRatePerMin: Int) {
self.heartRatePerMin = heartRatePerMin
}
}
class Animal: Living {
let name: String
init(name: String, heartRatePerMin: Int) {
self.name = name
super.init(heartRatePerMin: heartRatePerMin)
}
}
class Cat: Animal {
let meowsPerHour: Int
init(meowsPerHour: Int, name: String) {
self.meowsPerHour = meowsPerHour
super.init(name: name, heartRatePerMin: 60)
}
}
class Dog {
let runningSpeed: Int
init(runningSpeed : Int) {
self.runningSpeed = runningSpeed
}
}
Во-первых, когда я создаю экземпляры животных, собак и кошек. Я получаю предупреждения о том, всегда ли проверка будет успешной или неудачной. Как я проверяю типы и получаю предупреждения:
let animal = Animal(name: "Daisy", heartRatePerMin: 80)
let dog = Dog(runningSpeed: 2)
let cat = Cat(meowsPerHour: 10, name: "Smokey")
if animal is Animal {
//Warning: 'is' test always true
}
if dog is Animal {
//Warning: Cast from 'Dog' to unrelated type 'Animal' always fails
}
if cat is Animal {
//Warning: 'is' test always true
}
if cat is Living {
//Warning: 'is' test always true
}
Пока что я считаю, что компилятор проверяет, есть ли у данного класса объекта наследование проверяемому типу, и может выдавать предупреждения.
Затем я создал пустой протокол Grumpy:
protocol Grumpy {
}
После этого я соответствовал Grumpy из моего класса Cat, но я не соответствовал своему классу Dog.
Теперь, когда я проверяю, является ли тип моих объектов (кошка и собака) Grumpy следующим образом, я получаю предупреждение для кошки, поскольку класс Cat соответствует протоколу Grumpy, но я не получаю никакого предупреждения для собаки. Это ничего не говорит. Но когда я проверил для собаки, если это тип животного выше, он предупреждал, что всегда будет терпеть неудачу. Почему он не может предоставить ту же информацию для этого случая?
if cat is Grumpy {
//Warning: 'is' test is always true
}
if dog is Grumpy {
//Nothing
}
Затем, учитывая отсутствие у меня знаний CS и Swift, я также попытался сделать что-то еще, чтобы посмотреть, как он будет себя вести, я создал массив Any как anyArray. Затем создали переменную unknown, преобразовали cat в Any, чтобы она была равна unknown. Затем добавили unknown к anyArray. После этого я попытался ввести проверку, является ли первый индекс anyArray типом Cat, но снова без предупреждения:
var anyArray = [Any]()
let unknown = cat as Any
anyArray.append(unknown)
if anyArray[0] is Cat {
//Nothing.
}
Итак, учитывая все, что я пробовал, мне любопытно, как работает проверка типов как во время компиляции, так и во время выполнения? Я знаю, что это был довольно длинный вопрос, но мы будем благодарны за любой ответ.





Я предполагаю, что вы знаете, что следующее действительно и почему:
// although living is of type Living, it stores an instance of Cat at runtime
let living: Living = Cat(meowsPerHour: 10, name: "Smokey")
Давайте сначала рассмотрим это:
if dog is Grumpy {
//Nothing
}
dog относится к типу Dog, но компилятор не знает, сохранит ли во время выполнения dog подкласс Dog, который соответствует Grumpy. Оператор if будет запущен, если dog назначен следующим образом:
class GrumpyDog : Dog, Grumpy {
// ...
}
dog = GrumpyDog(...)
if anyArray[0] is Cat {
//Nothing.
}
Это потому, что anyArray[0] относится к типу Any. Компилятор недостаточно умен, чтобы знать, что во время выполнения anyArray[0] хранит Cat. Следовательно, нельзя быть уверенным, что is будет оценивать как истину. "Что, если это на самом деле Dog?" можно сказать.
@emrepun Когда мы запускаем приложение, задействуется компилятор. Компиляторы компилируют исходный код. Они не запускают программы. Во время выполнения среда выполнения точно знает, какой тип хранит каждая переменная. Нет необходимости выделять память. Среда выполнения просто проверяет, хранит ли anyArray[0] экземпляр Cat или один из подклассов Cat.
Спасибо за ваш ответ, но, например, в последний момент, поскольку компилятор недостаточно умен, когда мы запускаем приложение, компилятор пытается преобразовать этот объект как Cat под капотом? Если да, то как это сделать? Освободить место в памяти, чтобы его отлить? Затем, если это удастся или не удастся, удалит ли он то пространство в памяти, которое использовалось для проверки типа? @Sweeper