[swift-users] odd `==` `!=` behavior with class that inherits `NSObject`

Zhao Xin owenzx at gmail.com
Wed Apr 19 03:59:32 CDT 2017


Hi Nate,

It my personal habit to use do-block to compare different Objects with the
same name. For example,

do {

    class A {

        // some staff

    }

    let a = A()

    // some other staff

}


do {

    class A {

        func foo() {}

        // some staff

    }

    let a = A()

    // some other staff

}

I don't need to comment/uncomment with do-block when testing my code.

I think for NSObject's `isEqual(_)`, there is a big change and the change
is never mentioned in any docs. As far as I know, `==` had never worked
with NSObject instances in the past. Because only `isEqual(_)` is used.
`==` wouldn't be called at all.

In today's examples, `==` override the behavior `isEqual(_)`. The change is
huge. Because if `==` is never called, someone like me will notice and then
change his own code to override `isEqual(_)`. Now `isEqual(_)` is some how
abandoned if `==` and `!=` is implemented. That never happens before.

// prior Swift, NSObject instances
find code `a==b`, call `a.isEqual(b)`

// now Swift,
find code `a==b`, call ` if has operator `==``, call `a==b`; else call
`a.isEqual(b)`


Zhaoxin

On Wed, Apr 19, 2017 at 11:31 AM, Nate Birkholz <nbirkholz at gmail.com> wrote:

> I'm not sure exactly how class declarations interact with the scope of a
> do{ } statement. I declare a new static function inside your do{ } scope
> and it works fine, but something about the mapping of `==` and `!=` to
> `isEqual` in NSObject seems to be confused by the scope of the do{ } block.
>
> I'm not sure what the use case is for declaring a class inside a do{ }
> block, but it seems like a bug with SNObject operator mapping:
>
> //: Playground - noun: a place where people can play
>
>
> import UIKit
>
>
> do{
>
> class Foo:NSObject {
>
>     let name:String
>
>
>     init(name:String) {
>
>         self.name = name
>
>     }
>
>
>     public static func testStatic(_ object: Foo) {
>
>         print(object.name)
>
>     }
>
>
>     public static func ==(lhs: Foo, rhs: Foo) -> Bool {
>
>         guard type(of:lhs) == type(of:rhs) else { return false }
>
>         return lhs.name == rhs.name
>
>     }
>
>
>     public static func !=(lhs: Foo, rhs: Foo) -> Bool {
>
>         guard type(of:lhs) == type(of:rhs) else { return false }
>
>         return lhs.name != rhs.name
>
>     }
>
>
>     override func isEqual(_ object: Any?) -> Bool {
>
>         print("in isEqual")
>
>         guard let object = object as? Foo else { return false }
>
>         let _ = super.isEqual(object)
>
>         if self.name == object.name {
>
>             return true
>
>         }
>
>
>         return false
>
>     }
>
> }
>
>
>     let a = Foo(name: "bar")
>
>     let b = Foo(name: "bar")
>
>
>     print(a == b) // true
>
>     print(a != b) // false
>
>
>     Foo.testStatic(a) // bar
>
> }
>
>
> output is:
>
> in isEqual
>
> true
>
> in isEqual
>
> false
>
> bar
>
>
> For the record, enclosing just the statements *outside* the class
> declaration in a do{ } block works as expected:
>
> class Foo:NSObject {
>
>     let name:String
>
>
>     init(name:String) {
>
>         self.name = name
>
>     }
>
>
>     public static func ==(lhs: Foo, rhs: Foo) -> Bool {
>
>         guard type(of:lhs) == type(of:rhs) else { return false }
>
>         return lhs.name == rhs.name
>
>     }
>
>
>     public static func !=(lhs: Foo, rhs: Foo) -> Bool {
>
>         guard type(of:lhs) == type(of:rhs) else { return false }
>
>         return lhs.name != rhs.name
>
>     }
>
>
>     override func isEqual(_ object: Any?) -> Bool {
>
>         print("in isEqual")
>
>         guard let object = object as? Foo else { return false }
>
>         let _ = super.isEqual(object)
>
>         if self.name == object.name {
>
>             return true
>
>         }
>
>
>         return false
>
>     }
>
> }
>
> do{
>
>     let a = Foo(name: "bar")
>
>     let b = Foo(name: "bar")
>
>
>     print(a == b) // true
>
>     print(a != b) // false
>
> }
>
>
> output:
>
> true
>
> false
>
>
> On Tue, Apr 18, 2017 at 8:07 PM, Zhao Xin <owenzx at gmail.com> wrote:
>
>> Just put your code in to a do-block. The result will be opposite.
>>
>>
>> do {
>>
>>
>>
>>     class Foo:NSObject {
>>
>>         let name:String
>>
>>
>>
>>         init(name:String) {
>>
>>             self.name = name
>>
>>         }
>>
>>
>>
>>         public static func ==(lhs: Foo, rhs: Foo) -> Bool {
>>
>>             guard type(of:lhs) == type(of:rhs) else { return false }
>>
>>             return lhs.name == rhs.name
>>
>>         }
>>
>>
>>
>>         public static func !=(lhs: Foo, rhs: Foo) -> Bool {
>>
>>             guard type(of:lhs) == type(of:rhs) else { return false }
>>
>>             return lhs.name != rhs.name
>>
>>         }
>>
>>     }
>>
>>
>>
>>     let a = Foo(name: "bar")
>>
>>     let b = Foo(name: "bar")
>>
>>
>>
>>     print(a == b) // true, now false
>>
>>     print(a != b) // false, now true
>>
>> }
>>
>>
>> Zhaoxin
>>
>> On Wed, Apr 19, 2017 at 11:04 AM, Nate Birkholz <nbirkholz at gmail.com>
>> wrote:
>>
>>> Your issue seems to be that you created a custom implementation for the
>>> `==` operator but not one for the `!=` operator. If I add a custom
>>> implementation for `!=` I get the results you expected. Tthe default
>>> implementation of NSObject's `isEqual` is a test for identity, like the
>>> `===` in Swift. So when you ask "is `a` NOT the same object in memory as
>>> `b`?" the object returns "true" because they are indeed not the same object.
>>>
>>> class Foo:NSObject {
>>>
>>>     let name:String
>>>
>>>
>>>     init(name:String) {
>>>
>>>         self.name = name
>>>
>>>     }
>>>
>>>
>>>     public static func ==(lhs: Foo, rhs: Foo) -> Bool {
>>>
>>>         guard type(of:lhs) == type(of:rhs) else { return false }
>>>
>>>         return lhs.name == rhs.name
>>>
>>>     }
>>>
>>>
>>>     public static func !=(lhs: Foo, rhs: Foo) -> Bool {
>>>
>>>         guard type(of:lhs) == type(of:rhs) else { return false }
>>>
>>>         return lhs.name != rhs.name
>>>
>>>     }
>>>
>>> }
>>>
>>>
>>> let a = Foo(name: "bar")
>>>
>>> let b = Foo(name: "bar")
>>>
>>>
>>> print(a == b) // true
>>>
>>> print(a != b) // false
>>>
>>> On Tue, Apr 18, 2017 at 7:33 PM, Zhao Xin via swift-users <
>>> swift-users at swift.org> wrote:
>>>
>>>> Sample 1: both `==` and `!=` is true.
>>>>
>>>> import Foundation
>>>>
>>>>
>>>> class Foo:NSObject {
>>>>
>>>>     let name:String
>>>>
>>>>
>>>>     init(name:String) {
>>>>
>>>>         self.name = name
>>>>
>>>>     }
>>>>
>>>>
>>>>     public static func ==(lhs: Foo, rhs: Foo) -> Bool {
>>>>
>>>>         guard type(of:lhs) == type(of:rhs) else { return false }
>>>>
>>>>         return lhs.name == rhs.name
>>>>
>>>>     }
>>>>
>>>> }
>>>>
>>>>
>>>> let a = Foo(name: "bar")
>>>>
>>>> let b = Foo(name: "bar")
>>>>
>>>>
>>>> print(a == b) // true
>>>>
>>>> print(a != b) // true
>>>>
>>>> Sample 2: Add above code to a do-block, behavior changes to expect
>>>>
>>>> do {
>>>>
>>>>     class Foo:NSObject {
>>>>
>>>>         let name:String
>>>>
>>>>
>>>>
>>>>         init(name:String) {
>>>>
>>>>             self.name = name
>>>>
>>>>         }
>>>>
>>>>
>>>>
>>>>         public static func ==(lhs: Foo, rhs: Foo) -> Bool {
>>>>
>>>>             guard type(of:lhs) == type(of:rhs) else { return false }
>>>>
>>>>             return lhs.name == rhs.name
>>>>
>>>>         }
>>>>
>>>>     }
>>>>
>>>>
>>>>
>>>>     let a = Foo(name: "bar")
>>>>
>>>>     let b = Foo(name: "bar")
>>>>
>>>>
>>>>
>>>>     print(a == b) // false
>>>>
>>>>     print(a != b) // true
>>>>
>>>> }
>>>>
>>>> Sample 3: A little investigation shows that `==` didn't call NSObject's
>>>> `func isEqual(_ object: Any?) -> Bool` but `!=` did.
>>>>
>>>> class Foo:NSObject {
>>>>
>>>>     let name:String
>>>>
>>>>
>>>>
>>>>     init(name:String) {
>>>>
>>>>         self.name = name
>>>>
>>>>     }
>>>>
>>>>
>>>>
>>>>     public static func ==(lhs: Foo, rhs: Foo) -> Bool {
>>>>
>>>>         guard type(of:lhs) == type(of:rhs) else { return false }
>>>>
>>>>         return lhs.name == rhs.name
>>>>
>>>>     }
>>>>
>>>>
>>>>
>>>>     override func isEqual(to object: Any?) -> Bool {
>>>>
>>>>         print("111")
>>>>
>>>>         return super.isEqual(to: object)
>>>>
>>>>     }
>>>>
>>>>
>>>>
>>>>     override func isEqual(_ object: Any?) -> Bool {
>>>>
>>>>         print("2222")
>>>>
>>>>         return super.isEqual(object)
>>>>
>>>>     }
>>>>
>>>> }
>>>>
>>>>
>>>> let a = Foo(name: "bar")
>>>>
>>>> let b = Foo(name: "bar")
>>>>
>>>>
>>>> print(a == b) // true
>>>>
>>>> print(a != b) // 2222, true
>>>>
>>>> print(!(a == b)) // false
>>>>
>>>>
>>>> So I am wondering what is the future? Will we keep on using `isEqual(_
>>>>  object: Any?)` with class that inherits `NSObject`, or we are trying
>>>> to drop it?
>>>>
>>>> Xcode  8.3.1 (8E1000a), 3.1 (swiftlang-802.0.51 clang-802.0.41), macOS 10.12.4
>>>> (16E195)
>>>>
>>>> Zhaoxin
>>>>
>>>> _______________________________________________
>>>> swift-users mailing list
>>>> swift-users at swift.org
>>>> https://lists.swift.org/mailman/listinfo/swift-users
>>>>
>>>>
>>>
>>>
>>> --
>>> Nate Birkholz
>>>
>>
>>
>
>
> --
> Nate Birkholz
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170419/c53f3136/attachment.html>


More information about the swift-users mailing list