How can I update this Hashable.hashValue to confor

2019-08-04 01:34发布

问题:

I'm trying to fix up an old tutorial from RayWenderlich's site, no longer supported. The warning appears in three files, Chain.swift, Cookie.swift and Swap.swift from the "How to Make a Game Like Candy Crush with SpriteKit and Swift:" tutorial

I'm at a loss even after exploring the available replies that appear in many places. I'm struggling to understand just what this code is doing so that I can fix it. I know it's just a warning, and I can probably ignore it, but the game is also showing X where blank tiles should appear so I suspect that it may have something to do with this?

The warning is this:

'Hashable.hashValue' is deprecated as a protocol requirement; conform type 'Chain' to 'Hashable' by implementing 'hash(into:)' instead

File example

  class Chain: Hashable, CustomStringConvertible {
  var cookies: [Cookie] = []
  var score = 0

  enum ChainType: CustomStringConvertible {
    case horizontal
    case vertical

    var description: String {
      switch self {
      case .horizontal: return "Horizontal"
      case .vertical: return "Vertical"
      }
    }
  }

  var chainType: ChainType
  init(chainType: ChainType) {
    self.chainType = chainType
  }

  func add(cookie: Cookie) {
    cookies.append(cookie)
  }

  func firstCookie() -> Cookie {
    return cookies[0]
  }

  func lastCookie() -> Cookie {
    return cookies[cookies.count - 1]
  }

  var length: Int {
    return cookies.count
  }

  var description: String {
    return "type:\(chainType) cookies:\(cookies)"
  }

  var hashValue: Int {
    return cookies.reduce (0) { $0.hashValue ^ $1.hashValue }
  }

  static func ==(lhs: Chain, rhs: Chain) -> Bool {
    return lhs.cookies == rhs.cookies
  }
}

回答1:

For the same example: How to combine 2 conditions, e.g.

func ==(lhs: Cookie, rhs: Cookie) -> Bool {
return lhs.column == rhs.column && lhs.row == rhs.row

}

This will not work:

func hash(into hasher: inout Hasher) {
        hasher.combine(column) && hasher.combine(row)
    }

But

func hash(into hasher: inout Hasher) {
            hasher.combine(self)
        }

will compare everything?

Or even more complicated for a interated compare function:

func ==(lhs: SwapN, rhs: SwapN) -> Bool {
    if lhs.cookies.count != rhs.cookies.count {
        return false
    }

    for i in 0..<lhs.cookies.count {
        if lhs.cookies[i] != rhs.cookies[i] {
            return false
        }
    }
    return true
}

How should that be transfered?



回答2:

From the Hashable documentation:

Hashing a value means feeding its essential components into a hash function, represented by the Hasher type. Essential components are those that contribute to the type’s implementation of Equatable. Two instances that are equal must feed the same values to Hasher in hash(into:), in the same order.

And from the hash(into:) documentation:

The components used for hashing must be the same as the components compared in your type’s == operator implementation. Call hasher.combine(_:) with each of these components.

The implementation of

static func ==(lhs: Chain, rhs: Chain) -> Bool {
    return lhs.cookies == rhs.cookies
}

shows that cookies is the “essential component” which determines equality of instances. Therefore

func hash(into hasher: inout Hasher) {
    hasher.combine(cookies)
}

is a valid (and sensible) implementation of the Hashable requirement.