Приведение переменной в качестве экземпляра протокола не того же базового типа, что и переменная экземпляра класса?

#swift #generics #swift-protocols

#swift #общие сведения #swift-протоколы

Вопрос:

Учитывая, что базовые типы одинаковы, я ожидал test2 , что будет true , не false :

 protocol Foo {}
class Bar: NSObject, Foo {}
class Test {
    func testCompare() {
        let b = Bar()
        let test1 = compare(expected: Bar.self, actual: b)
        let c: Foo = b
        let test2 = compare(expected: Bar.self, actual: c)
        /*
         (lldb) p expected
         (@thick NSObject.Type) $R0 = SpokestackTests.Bar
         (lldb) p type(of: actual).self
         (SpokestackTests.Foo.Type) $R2 = SpokestackTests.Bar
        */
        print(test1, test2) // true false
    }
    
    func compare<T>(expected: NSObject.Type, actual: T) -> Bool {
        return expected == type(of: actual).self
    }
}
  

Связано ли это с различием между конкретным метатипом класса и экзистенциальным метатипом экземпляра протокола?

Ответ №1:

После ознакомления с документацией функции type(of:) мы можем перефразировать последний абзац примерно так:

Этот неожиданный результат возникает из-за того, что вызов type(of: value) inside compare(expected:actual:) должен возвращать метатип, экземпляром T.Type которого является статический тип actual параметра ( Foo.self ) . Чтобы получить динамический тип внутри значения в этом общем контексте, приведите параметр к любому при вызове type(of:) .

или просто измените compare(expected:actual:) функцию, чтобы использовать Any type вместо generics:

 private func compare(expected: NSObject.Type, actual: Any) -> Bool {
    return expected == type(of: actual).self
}
  

Обновление: еще лучше, вы можете использовать предложение @Jessy в комментариях

 func compare<Expected: Foundation.NSObject>(expected: Expected.Type, actual: Any) -> Bool {
    return type(of: actual) is Expected.Type
}
  

Комментарии:

1. Вероятно, лучше выразить как : func compare<Expected: Foundation.NSObject>( expected _: Expected.Type, actual: Any ) -> Bool { type(of: actual) is Expected.Type }

2. @Jessy да, это, вероятно, лучше. Я обновил свой ответ. Спасибо.

3. Является ли объяснение type(of:) поведения, которое, поскольку Any является как типом значения, так и ссылочным типом (и типом функции!), приведение actual типа к Any позволяет системе типов Swift разрешать разницу между конкретным метатипом класса и экзистенциальным метатипом экземпляра протокола?

4. @Noel да. Приведение его как Any практически освобождается от общего контекста.

Ответ №2:

Он может сравнивать вашу функцию с тем же объектом сравнения

Он не может сравнивать метатип сравнения с экземпляром, поэтому возвращает false Эти значения также возвращают false, которые вы можете попробовать в pg :

     let d: Any = b
    _ = compare(expected: Bar.self, actual: d)
    
    d as! Foo
    _ = compare(expected: Bar.self, actual: d)