Difference between Swift's hash and hashValue - swift

The Hashable protocol in Swift requires you to implement a property called hashValue:
protocol Hashable : Equatable {
/// Returns the hash value. The hash value is not guaranteed to be stable
/// across different invocations of the same program. Do not persist the hash
/// value across program runs.
///
/// The value of `hashValue` property must be consistent with the equality
/// comparison: if two values compare equal, they must have equal hash
/// values.
var hashValue: Int { get }
}
However, it seems there's also a similar property called hash.
What is the difference between hash and hashValue?

hash is a required property in the NSObject protocol, which groups methods that are fundamental to all Objective-C objects, so that predates Swift.
The default implementation just returns the objects address,
as one can see in
NSObject.mm, but one can override the property
in NSObject subclasses.
hashValue is a required property of the Swift Hashable protocol.
Both are connected via a NSObject extension defined in the
Swift standard library in
ObjectiveC.swift:
extension NSObject : Equatable, Hashable {
/// The hash value.
///
/// **Axiom:** `x == y` implies `x.hashValue == y.hashValue`
///
/// - Note: the hash value is not guaranteed to be stable across
/// different invocations of the same program. Do not persist the
/// hash value across program runs.
open var hashValue: Int {
return hash
}
}
public func == (lhs: NSObject, rhs: NSObject) -> Bool {
return lhs.isEqual(rhs)
}
(For the meaning of open var, see What is the 'open' keyword in Swift?.)
So NSObject (and all subclasses) conform to the Hashable
protocol, and the default hashValue implementation
return the hash property of the object.
A similar relationship exists between the isEqual method of the
NSObject protocol, and the == operator from the Equatable
protocol: NSObject (and all subclasses) conform to the Equatable
protocol, and the default == implementation
calls the isEqual: method on the operands.

Related

Swift: different objects with same properties: hash value

Currently I have a class of generic type, and I want to make the object of this class searchable via
contains()
method for an array of those objects, by making the class conform to Hashable protocol and provide a hash value for each object. Now my problem is I have objects with exactly the same properties, and it seems that the array cannot really distinguish them (my current approach is to use one of the properties' hash value as the hash value for the class, and the
== <T> (lhs: ClassA<T>, rhs: ClassA<T>) -> Bool
function is done by comparing the hash value). I have tried to use a static property like "id", but for generic types static properties are not supported.
How should I define the hash value such that different objects with the same properties can still be differentiated?
EDIT: I'm making it conform to Hashable directly because it's also used as keys in dict in other parts of the program, since Hashable already conforms to Equatable.
My current approach is to use one of the properties' hash value as the
hash value for the class, and the
== <T> (lhs: ClassA<T>, rhs: ClassA<T>) -> Bool
function is done by comparing the hash value
That's not how the == and hashValue relationship works – don't do this. What if you get a hash collision? Two different instances with different properties could compare equal.
You should instead implement == to actually compare the properties of two instances. == should return true if two given instances have equivalent properties. The hashValues of two instances should be equivalent if they compare equal with ==.
Now, it might well be the case that you cannot do this comparison unless T is Equatable. One solution to this is to not conform ClassA to Equatable, but instead just overload == for when T is Equatable, such as:
func == <T : Equatable>(lhs: ClassA<T>, rhs: ClassA<T>) -> Bool {
// stub: do comparison logic
}
You can now just use Sequence's contains(where:) method in conjunction with the == overload in order to check if a given instance is in the array:
var array = [ClassA("foo")] // assuming ClassA has an init(_: T) and a suitable ==
// implementation to compare that value
let someInstanceToFind = ClassA("foo")
print(array.contains { $0 == someInstanceToFind }) // true
And if you want ClassA to have a hashValue, then simply write an extension that defines a hashValue when T is Hashable:
extension ClassA where T : Hashable {
var hashValue: Int {
return 0 // to do: implement hashValue logic
}
}
Unfortunately, this does mean that ClassA won't explicitly conform to Hashable when T does – but it will have a hashValue and == implementation. SE-0143: Conditional conformances will change this by allowing explicit conformance to protocols if a given where clause if satisfied, but this is yet to be implemented.
If you need explicit conformance to Hashable (such as for using instances of your class in a Set or as Dictionary keys) – then one solution is to create a wrapper type:
struct HashableClassA<T : Hashable> : Hashable {
var base: ClassA<T>
init(_ base: ClassA<T>) {
self.base = base
}
static func ==(lhs: HashableClassA, rhs: HashableClassA) -> Bool {
return lhs.base == rhs.base
}
var hashValue: Int {
return base.hashValue
}
}
Now you just have to wrap ClassA<T> instances in a HashableClassA instance before adding to a Set or Dictionary.
Just realized there is a simple way for achieving the Equatable in
contains()
method: use
return lhs === rhs
in the == function such that objects are compared directly. It's working in this way now.

Why does RangeReplaceableCollection require an empty initialiser?

RangeReplaceableCollection requires the implementation of init(). This empty initialiser seems to be used in the default implementations of init(_:), init(repeating:count:) and removeAll(keepingCapacity:), of which the latter should also be implementable with removeSubrange(_:).
Why are these three initialisers part of RangeReplaceableCollection?
How I ran into this: I was making a collection that behaves like an array, but can be subscripted using any generic index that conforms to Strideable. It's declared like this:
public struct AnyIndexArray
<Index: Strideable, Element where Index.Stride == Int> {
private var elements: [Element]
public var startIndex: Index
public init<S: Sequence where S.Iterator.Element == Element>
(elements: S, startIndex: Index) {
self.elements = Array(elements)
self.startIndex = startIndex
}
}
Since the underlying array conforms to RangeReplaceableCollection, it would make sense to also conform AnyIndexArray to this protocol. However, I can't provide an empty initialiser because at the very least a startIndex is needed. If I implement the empty initialiser anyways and just put fatalError() in its body, everything works fine - except for the three initialisers and removeAll.
What is it about RangeReplaceableCollection that it needs an empty initialiser, while Collection doesn't?
"An empty initializer - is useful in generic functions, as it allows a function to create new empty collections of the same type" as objc.io "Advanced Swift" book mentions.
For me it sounds like implementation should be optional, but worth discussing it in swift-evolution group.
In your case why startIndex couldn't have default value 0 ?
Addition:
Actually it was already discussed here https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20160704/002510.html

Why does the following protocol have this required function?

The following code was mentioned at WWDC 2015:
protocol Drawable {
func isEqualTo(other: Drawable) -> Bool
func draw()
}
extension Drawable where Self : Equatable {
func isEqualTo(other: Drawable) -> Bool {
if let o = other as? Self { return self == o }
return false
}
}
I'm a little confused on this whole protocol extension thing. Why would they have isEqualTo(other: Drawable) -> Bool in the Drawable protocol and then only extend when self is equatable? Why should isEqualTo be a required method for all Drawable objects? From my view, if a new class/struct hasn't implemented Equatable, the objects don't have the capability to be logically checked for equality, so they couldn't implement an equatable method. I think it would make more sense to have it be an optional implementation. Where is the fault in my logic?
The problem being solved is a limitation of generics.
Let's say we have a Bird struct and Insect struct. A generic equatable lets us define == where the actual object types are the same type. So we can make Bird adopt Equatable so that if we have b1 and b2 both typed as Bird we can decide whether they are equal. And we can make Insect adopt Equatable so that if we have i1 and i2 both typed as Insect we can decide whether they are equal.
But now suppose both Bird and Insect adopt the Flier protocol. You cannot make Flier adopt Equatable, because there's a limitation in how generics work. So if two objects are typed as Flier, you have no way of implementing equatability for them.
The video demonstrates that protocol extensions solve this problem. With a protocol extension on Flier, you can define a different method that compares two Fliers and decides whether they are equal - namely, by deciding first whether they are of the same class and then applying ==. Thus you can make sense of equatability for a Flier (a protocol).
I can only guess as to why isEqualTo: is a required method for the drawable protocol. Perhaps so that whatever is drawing these things never wastes time drawing the same thing twice?
I can comment on the rest of it however.
Equatable is a Swift protocol (not available in Objective-C) which requires that there is a == operator defined for the type.
In Objective-C, there is no operator overloading. Moreover, in Objective-C, using == to compare objects simply compares their pointers. If the objects are the same object (same memory location), == returns true. If we want to see if the objects are different objects but still considered equal, we must use isEqualTo:. This is a method defined by NSObject and the default implementation simply returns the result of == comparison. But classes tend to override this.
In Swift, == has different behavior. In Swift, == returns behaves similarly to how we expect the isEqualTo: method to behave in Objective-C. That's because Swift has the === operator for comparing references. === returns true if these objects are the same (same memory location), but == is a custom implemented method that determines whether the objects are considered equal even if they are in different memory locations.
So I'm guessing the Drawable protocol has Objective-C classes in mind when it declares isEqualTo: as one of its required methods.
We could alternatively write the Drawable protocol as such:
protocol Drawable: Equatable {
func draw()
}
From a strictly Swift perspective, this is a roughly equivalent protocol definition. But this means that whoever is using Drawable objects expects to compare them with == in Swift rather than isEqualTo:. And moreover, this means if we want to use any Objective-C defined objects with the protocol, now we must implement a custom == function for each of them, which most likely looks like this:
func == (left ObjCClass, right ObjCClass) -> Bool {
return left.isEqualTo(right)
}
But we have to do this for every Objective-C class we want to define as Drawable.
The alternative is to define our protocol as you presented it, using isEqualTo:, a very commonplace Objective-C method.
Now, to make all of our Swift types conform to Drawable, all we have to do is implement the draw() method and conform to Equatable. As long as we conform to Equatable, the extension will add the isEqualTo: method to our Swift type as a simple return left == right effectively (and the existence of the == method is what the Equatable protocol guarantees).

How to represent an optional Bool (Bool?) in Objective-C?

I am trying to write a protocol in swift
#objc protocol RestAPIManagerDelegate {
optional func credentialValidated(isValid: Bool?)
}
But I am getting following error:
'Method cannot be marked #objc because the type of the parameter cannot be represented in Objective-C'
Any suggestion?
The problem is this type declaration:
`isValid: Bool?`
That is perfectly fine in Swift. But you cannot expose it to Objective-C, because Objective-C does not have any notion of an Optional BOOL - a BOOL in Objective-C is basically just a number, a primitive C datatype (what we call a scalar).
Here's another way of looking at it. In an interchange with Objective-C, you can use a Swift Optional anywhere that Objective-C can say nil - indeed, to a large extent Swift Optional exists exactly in order to deal with the possibility that Objective-C will say nil or that you might need to say nil to Objective-C. But a BOOL in Objective-C can never be nil - it can only be YES or NO (Swift true or false).
So you have three choices:
Take away the #objc that exposes all this to Objective-C
Remove the Optional and just declare that type a Bool
Use an object type. For example, declare the type as AnyObject? (or NSNumber?). This will work because Swift will bridge a Bool to an NSNumber (including as it passes into an AnyObject), and Objective-C will deal just fine with an Optional AnyObject or Optional NSNumber because those are object types, not scalars.
Object-C does not have the concept of optionals, try to remove the "?" from your declaration
It's OK to use NSNumber? instead, but there is another way.
You can create OptionalBool enum and use it instead of Bool? for compatibility with Objective-C.
Creating OptionalBool enum in Swift code:
#objc enum OptionalBool: Int {
case none
case yes
case no
}
#objc protocol RestAPIManagerDelegate {
#objc optional func credentialValidated(isValid: OptionalBool)
}
Using OptionalBool in Objective-C code:
#interface RestAPIManagerHandler () <RestAPIManagerDelegate>
#end
#implementation RestAPIManagerHandler
- (void)credentialValidatedWithIsValid:(enum OptionalBool)isValid {
switch (isValid) {
case OptionalBoolYes:
NSLog(#"TRUE");
break;
case OptionalBoolNo:
NSLog(#"FALSE");
break;
case OptionalBoolNone:
NSLog(#"NULL");
break;
}
}
#end
Swift Bool are converted in NSNumber so for example if a Swift method returns [Bool] and if you receive this array in Objective-C code, it become a NSArray <NSNumber *>. And if you give this array to a Swift method which receives a [Bool] as parameter, the conversion will be done automatically.
If you are wanting to just do a protocol in Swift, for use within Swift, without optionals you can do this:
public protocol Note {
var content: [ContentType] {get}
func insertNote(note: Note) -> Bool
}
That will force a class to implement a method that returns a Bool and have an array of some class called ContentType.
If you want to make the method optional, you must specify the objc keyword as you have done, regardless if you are interoperating with Objective-C or not. At that point, everything else should work in your example.
#objc protocol RestAPIManagerDelegate {
optional func credentialValidated(isValid: Bool?)
}
From Apples documentation:
Optional protocol requirements can only be specified if your protocol is marked with the #objc attribute.
This attribute indicates that the protocol should be exposed to Objective-C code and is described in Using Swift with Cocoa and Objective-C. Even if you are not interoperating with Objective-C, you need to mark your protocols with the #objc attribute if you want to specify optional requirements.
Note also that #objc protocols can be adopted only by classes, and not by structures or enumerations. If you mark your protocol as #objc in order to specify optional requirements, you will only be able to apply that protocol to class types.
The example given by Apple:
#objc protocol CounterDataSource {
optional func incrementForCount(count: Int) -> Int
optional var fixedIncrement: Int { get }
}

SequenceType implementation

SequenceType in Swift implements _Sequence_Type but these two protocols seem basically the same. Why is it implemented this way?
protocol SequenceType : _Sequence_Type {
typealias Generator : GeneratorType
func generate() -> Generator
}
protocol _SequenceType {
}
protocol _Sequence_Type : _SequenceType {
/// A type whose instances can produce the elements of this
/// sequence, in order.
typealias Generator : GeneratorType
/// Return a generator over the elements of this sequence. The
/// generator's next element is the first element of the sequence.
func generate() -> Generator
}
Protocols with leading underscores are Apple-private. The header for them typically does not include all of their actual methods. You can reverse engineer their actual methods by declaring a class to implement them, and see what the compiler says is missing. (The results are of course completely unsupported, and very likely to change between releases, which is why the particular protocol is likely private.)
In short, the answer is "because we don't actually know what's in those protocols."