== not be called, why?

I have a class A: NSObject, NSCoding {

and inside I define

static func == (lhs: A, rhs: A) -> Bool.

}


a: A

b: A

when I do a == b, func == was not called, why?

Answered by OOPer in 319630022

If using isEqual, I need lots of lines code as below?

No. As I wrote

When Swift imports `NSObject`, it adds a definition of `==` for NSObject which internally calls isEqual(_:) method.


You have no need to call `isEqual` explicitly, and all Optional things are handled in the Standard Library as QuinceyMorris wrote, just if you do the right thing.


You can easily test the code below in the Playground, or making a Command Line Tool project.

import Foundation

class A: NSObject {
    var n: Int = 0
   
    override func isEqual(_ object: Any?) -> Bool {
        print(#function, "called")
        if let other = object as? A {
            return self.n == other.n
        } else {
            return false
        }
    }
   
}

let a = A()
let b = A()

print(a == b)
print(a != b)

let optA: A? = A()
let optB: A? = A()

print(optA == optB)
print(optA != optB)


print(a == optB)
print(optA == b)


No need to define `==`, `!=`, no need to worry about Optionals.

Show your actual code, using the <> code editor, rather than paraphrased pseudo code, thanks.

FWIW, it works for me, so (as KMT says) show some actual code -- preferably something that fails when running in a playground.

How do you know it is not called ?

I set the breakpoint there, it was never reached.

I tested in simpler class, it works, but not in my class.

Was there anything prevent it happening?

I had to define a function equal inside class A and use a.equal(b)

This is a known issue.


When you have a class hierarchy, static functions aren't overridden, this interacts badly with Equatable protocol conformance, which is how the compiler finds a custom '==' function.


The correct solution is what you did: implement the "guts" of the equality test in a regular method, and have your '==' function call that method.


There's no reasonable way you could have known about this quirk in advance, so I'm glad you found this correct solution.

In further courious testing, I found if the class had an init? initializer,

== will never be called, otherwise it would be called.

This solution also have pitfalls, when comparing two data objects.

how do you acheive

1. nil == nil

2. nil <> non_nil

3. compare two non_nils?

The question became how to gracefully compare two optionals in Swift?

Are you sure that the types of the both hand sides are exactly `A` ? Are they not `Optional<A>` (`A?`) nor any subclasses of `A` ?


And you need to consider which `==` is called when your `==` is not called.


When Swift imports `NSObject`, it adds a definition of `==` for NSObject which internally calls isEqual(_:) method.

So, the right way of implementing your own equality for `NSObject` descendants, is to override isEqual(_:).


class A: NSObject {
   
    override func isEqual(_ object: Any?) -> Bool {
        if let other = object as? A {
            return ...
        } else {
            return false
        }
    }
   
}


You may need to override the hash() method of `NSObject` (not hashValue of Swift `Hashable`), but that may be another issue.

FWIW, the compiler handles this for you. (It's not a language feature, exactly, just a special case in the compiler behavior.)


If there's a custom '==' function that works for a combination of NON-optional types, then the compiler will use it for comparisons of the corresponding OPTIONAL types. So, if both are nil, it succeeds, if one of the two is nil, it fails, and if both are non-nil, it uses your custom function.


You don't need to do anything to get this behavior.

Obviously it didn't happen, how can I make it happen?

to let the optionals class also calling the ==?

In your case the `==` operator for Optional does not work as you expect.


It's defined in the Swift Standard Library as follows:

extension Optional : Equatable where Wrapped : Equatable {
    
    public static func == (lhs: Wrapped?, rhs: Wrapped?) -> Bool

}


in the expression `a == b` (where a or b is of type `A?`), Swift infers the generic parameter `Wrapped` (which needs to be `Equatable`) as `NSObject`.


Thus, Swift uses `==` for `NSObject`, not your `==` for `A`.


Do the right thing as I wrote in another post.

I was thinking one line could achieve the goal gracefully. Is it possible?

var a: A, b: A

a == b

If using isEqual, I need lots of lines code as below?

if let val = a as? A {

return a.isEqual(b)

}

else if let val = b as? A {

return false

}

else

return true //both nil

Accepted Answer

If using isEqual, I need lots of lines code as below?

No. As I wrote

When Swift imports `NSObject`, it adds a definition of `==` for NSObject which internally calls isEqual(_:) method.


You have no need to call `isEqual` explicitly, and all Optional things are handled in the Standard Library as QuinceyMorris wrote, just if you do the right thing.


You can easily test the code below in the Playground, or making a Command Line Tool project.

import Foundation

class A: NSObject {
    var n: Int = 0
   
    override func isEqual(_ object: Any?) -> Bool {
        print(#function, "called")
        if let other = object as? A {
            return self.n == other.n
        } else {
            return false
        }
    }
   
}

let a = A()
let b = A()

print(a == b)
print(a != b)

let optA: A? = A()
let optB: A? = A()

print(optA == optB)
print(optA != optB)


print(a == optB)
print(optA == b)


No need to define `==`, `!=`, no need to worry about Optionals.

Do I need to override the hash() method of `NSObject`? Is it ok not to?

== not be called, why?
 
 
Q