Как работает проверка типов в Swift

Мне любопытно, как проверка типов работает в 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.
    }

Итак, учитывая все, что я пробовал, мне любопытно, как работает проверка типов как во время компиляции, так и во время выполнения? Я знаю, что это был довольно длинный вопрос, но мы будем благодарны за любой ответ.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
139
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я предполагаю, что вы знаете, что следующее действительно и почему:

// 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?" можно сказать.

Спасибо за ваш ответ, но, например, в последний момент, поскольку компилятор недостаточно умен, когда мы запускаем приложение, компилятор пытается преобразовать этот объект как Cat под капотом? Если да, то как это сделать? Освободить место в памяти, чтобы его отлить? Затем, если это удастся или не удастся, удалит ли он то пространство в памяти, которое использовалось для проверки типа? @Sweeper

emrepun 18.12.2018 10:48

@emrepun Когда мы запускаем приложение, задействуется компилятор. Компиляторы компилируют исходный код. Они не запускают программы. Во время выполнения среда выполнения точно знает, какой тип хранит каждая переменная. Нет необходимости выделять память. Среда выполнения просто проверяет, хранит ли anyArray[0] экземпляр Cat или один из подклассов Cat.

Sweeper 18.12.2018 10:52

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