Как я могу заставить CMTime соответствовать Hashable на всех версиях iOS?

В Xcode 14 Apple добавила соответствие Hashable иCMTimeRangeтолько для iOS16. Я пытаюсь сделать его Hashable для всех версий iOS, потому что у нас есть много структур Hashable, которые содержат , и они также зависят от того, являются ли они Hashable.

До сих пор у нас было расширение, которое соответствовало Hashable, но в Xcode 14 это расширение вызывает ошибку компиляции с этим описанием:

      Protocol 'Hashable' requires 'hashValue' to be available in iOS 14.0.0 and newer

Если я реализую так:

        public var hashValue: Int {
    var hasher = Hasher()
    hash(into: &hasher)
    return hasher.finalize()
  }

Он компилируется и работает, но я не уверен, что это безопасно, потому чтоhashValueустарела, поэтому я не уверен, что понимаю, зачем это нужно (толькоhash(into:)должны быть реализованы для соответствия Hashable в наши дни).

Может ли кто-нибудь пролить свет на то, безопасно ли это решение или есть какое-либо другое решение?

Еще одна идея, которую я пробовал:

Я добавил это расширение на:

      extension CMTime {
  struct Hashed: Hashable {
    private let time: CMTime

    init(time: CMTime) {
      self.time = time
    }

    public func hash(into hasher: inout Hasher) {
      // pre iOS16 hash computation goes here.
    }
  }

  func asHashable() -> Hashed {
    return Hashed(time: self)
  }
}

Затем я изменил все структуры Hashable, содержащиеCMTimeиз этого:

      struct Foo: Hashable {
  let time: CMTime

  let string: String
}

к этому:

      struct Foo: Hashable {
  let time: CMTime

  let string: String

  func hash(into hasher: inout Hasher) {
    if #available(iOS 16, *) {
      hasher.combine(self.time)
    } else {
      hasher.combine(self.time.asHashable())
    }
    hasher.combine(self.string)
  }
}

Я не фанат этого, так как он внесет МНОГО изменений в код.

1 ответ

РЕДАКТИРОВАТЬ 2:

Приведенный ниже код работает с симулятором iOS 16, но дает сбой с iOS 15 и более ранними версиями. Странно, так как он компилируется.

Я предлагаю реализовать расширение и сделать его доступным только для iOS 15 и ниже:

      @available(iOS, obsoleted: 16)
extension CMTime: Hashable {
    public var hashValue: Int {
        var hasher = Hasher()
        hash(into: &hasher)
        return hasher.finalize()
    }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(value)
        hasher.combine(timescale)
        // more if needed
    }
}

Что касается того, почемуhashValueвсе еще нужен, я согласен, что его там не должно быть. Возможно, это ложная тревога XCode, ошибка действительно сбивает с толку. Обратите внимание, что Apple не говорит, когда он устарел, а также он устарел как требование, я предполагаю, что он все еще за кулисами: https://developer.apple.com/documentation/swift/hashable/hashvalue

РЕДАКТИРОВАТЬ 1:

CMTimeуже соответствуетHashable. Протестировано с iOS 13, 14, 15 и 16. Я не думаю, что вам нужно ваше расширение.

      import Foundation
import AVFoundation

struct Foo: Hashable {
    let aaa: String
    let bbb: Int
    let time: CMTime
}

func testMyFoo() {
    let foo1 = Foo(
        aaa: "yo",
        bbb: 123,
        time: CMTime(
            value: 100,
            timescale: 1
        )
    )

    let foo2 = Foo(
        aaa: "yo",
        bbb: 123,
        time: CMTime(
            value: 100,
            timescale: 1
        )
    )

    var myFoos = Set<Foo>()
    myFoos.insert(foo1)
    myFoos.insert(foo2)

    print(myFoos)
    // you will only have 1 foo in the Set, because they are the same.
    // if you change the CMTime values in foo1, you will have 2 items in the Set.
}
Другие вопросы по тегам