About Swift, Hashable protocol compliance.

Asked 2 years ago, Updated 2 years ago, 137 views

I'd like to use my own class to process it using a set.
For that reason, I would like to conform to the Hashable protocol to my own model.

From the translated version of the swift reference,

structure GridPoint{
         varx —Int
         variable —Int
     }



extension GridPoint: Hashable {
         var hashValue: Int {
             return x.hashValue^y.hashValue
         }

         static func==(lhs:GridPoint, rhs:GridPoint) - >Bool{
             return lhs.x==rhs.x&lhs.y==rhs.y
         }
     }

This part is difficult to understand, so I asked you a question.

So what do we have to define?

swift swift5

2022-09-30 21:42

1 Answers

"

The answer to the ""self-made class"" was not the answer, so I added and revised it."

(Thanks to Nagonsoftware)

The Hashable chimo (which is a common concept in other languages with similar mechanisms) is

    Use
  • hashValue to check if it can be equal

    hashValue must be equal to never ==

  • If
  • hashValue is equal, use == to see if it is actually equal

Use hashValue to check if it can be equal.

hashValue must be equal to never ==

If hashValue is equal, use == to check if it is actually equal

That's how you use it.

For the current Swift, Hashable is defined as follows:

public protocol Hashable:Equatable{

    var hashValue: Int {get}

    funchash (into hasher:inout hasher)
}

Also, Equatable is defined as follows:

publicprotocolEquatable{
    static func==(lhs:Self, rhs:Self)->Bool
}

(The header document contained in Swift's own source contains very useful content, but it was omitted this time because it was all in English.)

In short, you can conform to the Hashable protocol by defining one method, one property, and one operator.

  • Properties to get hash values

  • Method for calculating hash values sequentially

  • Operator that determines equality==

Properties to retrieve hash values hashValue

Method for calculating hash values sequentially hash(into:)

Operator ==

to determine equivalence

Because Swift now has a default implementation for hashValue, you can only prepare hash(into:) and == to match your defined structure or class to Hashable.

(If you really want to do it yourself) Like this:

structure GridPoint{
    varx —Int
    variable —Int
}

extension GridPoint: Hashable {
    public funchash(into hash:inout Hasha) {
        x.hash (into: & hasher)
        y.hash (into: & hasher)
    }

    static func==(lhs:GridPoint, rhs:GridPoint) - >Bool{
        return lhs.x==rhs.x&lhs.y==rhs.y
    }
}

(Note: Implementing the hashValue directly instead of hash(into:) is a fairly old fashioned approach.It would be better to look for more new sources.)

One important point here is that hashValue and == must meet the following axioms in order to be consistent.

a==b will definitely pass a.hashValue==b.hashValue

It may be difficult to immediately understand that the stereotypical implementation described above satisfies this axiom, but it will work.(I'm not confident that I can write long but easy to understand, so I'll break it roughly.)

However, in Swift (4.2 and later), it is rare for structure and enum to write such a routine action yourself.

Do not use extension at all, but write the following definition somewhere:

structure GridPoint:Hashable{
    varx —Int
    variable —Int
}

The Swift compiler will accept GridPoint as the Hashable type to issue any errors or warnings.Of course, it can also be used as a Key for Set and Dictionary.

Suggestion SE-0185 Synthesizing Equatable and Hashable conformance (and subsequent fixes SE-0206 Hashable EnhaSince ncements) was adopted, the Swift compiler automatically creates definitions of hashValue, hash(into:) and == for the declared data type.Don't worry too much and keep using it.

So if you want to "make your own model conform to the Hashable protocol",

Add :Hashable to the

type declaration

That's what it means.

If you would like to know the background a little more deeply, or if the data type you define doesn't work, please let us know by commenting.If you want to use a set to process it, you should be able to see that it can be used for Set right away.

The hash(into:) method and the == operator must be implemented to be consistent, as described above.I don't know what your class is defined, so I have to show it in general cases, for example,

1. To determine == if the values of the properties that will be the two keys are equal.

class MyClass {
    varkeyValue1: Int
    varkeyValue2—String
    variableValue1: Double=0.0
    variable otherValue2: String = [ ]

    init(keyValue1:Int, keyValue2:String) {
        self.keyValue1 = keyValue1
        self.keyValue2 = keyValue2
    }
}

extension MyClass: Hashable {
    Define //`==` to match the meaning of what your class represents
    static func==(lhs:MyClass, rhs:MyClass) - >Bool{
        // If `keyValue1` and `keyValue2` are equal, the two `MyClass` are `==`.
        return lhs.keyValue1 == rhs.keyValue1 & lhs.keyValue2 == rhs.keyValue2
    }

    public funchash(into hash:inout Hasha) {
        // Call `hash(into:)` to the property used in `==` to determine whether it is equal or not
        keyValue1.hash (into: & hasher)
        keyValue2.hash (into: & hasher)
    }
}

Implement == as described above, and implement hash(into:) accordingly.

2. To determine == if you are referring to the same instance

// MyClass definition must be the same as above

extension MyClass: Hashable {
    static func==(lhs:MyClass, rhs:MyClass) - >Bool{
        // To define `==` if referring to the same instance
        return lhs === rhs
    }

    public funchash(into hash:inout Hasha) {
    // Since the address itself is used to determine whether it is equal or not, I will call `hash(into:)` for `ObjectIdentifier(self)`
        ObjectIdentifier(self).hash(into:&hasher)
    }
}

NSObject defaults to Hashable, but the default implementation of hashValue and == differs from the net Swift class as follows:

Therefore, if you customize the behavior, you must override the hash property instead of the hash(into:) operator and the isEqual(_:) method instead of the == operator.

class MyNSClass: The descendant class of the NSObject {//<-`NSObject` is always treated as `Hashable`
    varkeyValue1: Int
    varkeyValue2—String
    variableValue1: Double=0.0
    variable otherValue2: String = [ ]

    init(keyValue1:Int, keyValue2:String) {
        self.keyValue1 = keyValue1
        self.keyValue2 = keyValue2
    }

    // Override `isEqual(_:)` if you want to change the behavior of `==`
    override funcisEqual(_object:Any?) - >Bool{
        if let rhs = object as ?MyNSClass {
            return self.keyValue1 == rhs.keyValue1 & self.keyValue2 == rhs.keyValue2
        } else{
            return false
        }
    }

    // Make `hash` property consistent with `isEqual(_:)`
    override var hash:Int {
        var hasher=Hasher()
        keyValue1.hash (into: & hasher)
        keyValue2.hash (into: & hasher)
        return hasher.finalize()
    }
}

I don't understand the contents of your self-made class, so I could only write general things, so it might be a little difficult to see.If you have any questions about "self-made classes", you can get a better answer faster if you provide a definition of that class.


2022-09-30 21:42

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.