0

It seems that I've facing inconsistent behavior with Swift's indexOf function.

There are two indexOf functions in Swift:
1. The first one takes Equatable as an argument: array.indexOf(obj)
2. The second takes matching closure as an argument: array.indexOf{$0 == obj}

I have an NSObject subclass for which == operator is defined, i.e. it conforms to Equatable, therefore, I assume both function to work exactly the same (with closure in example).

However, the 1st method behaves inconsistently, in particular it returns nil when being invoked with object, instance of which is not present in array.

To illustrate issue, I provide example code with comments:

class EqObj: NSObject {
  let value: Int
  init(value: Int) {
    self.value = value
  }
}

func ==(lhs: EqObj, rhs: EqObj) -> Bool{
  return lhs.value == rhs.value
}

var array = [Obj(value: 1), Obj(value: 3), Obj(value: 5)]
var object = Obj(value: 5)

// returns nil, should return 2 - incorrect
array.indexOf(object) // Instance is not present
// returns 2, correct
array.indexOf(array.last!) // Instance is present
// returns 2, correct
array.indexOf{$0 == object} // Instance is not present, predicate
// returns non-empty array, correct
array.filter{$0 == object} // Instance is not present, predicate

The issue is reproducible only with NSObject subclasses. When I change Obj: NSObject to Obj: Equatable method indexOf() works exactly as expected, i.e. returns 2.

The question is whether this can be considered a bug?

My assumption is that array.indexOf(object) invokes isEqual method of NSObject and not my overloaded == operator.

My solution: I use array.indexOf{$0 == object}

Richard Topchii
  • 7,075
  • 8
  • 48
  • 115

1 Answers1

0

First of all with this definition I am getting different results

class Obj: NSObject {
    let value: Int

    init(value:Int) {
        self.value = value
    }
}

func ==(lhs: Obj, rhs: Obj) -> Bool{
    return lhs.value == rhs.value
}

var array = [Obj(value: 1), Obj(value: 3), Obj(value: 5)]
var object = Obj(value: 5)

here they are

enter image description here

You can read below the reasons after each result

Result #1

array.indexOf(object) // nil

In this case the isEqual method is used, so you are just comparing memory address. And since the object var references an object NOT inside the array, you get nil as result

Result #2

array.indexOf(array.last!) // 2

Again isEqual is used. However here you are passing to indexOf the reference to the last object in the array so it works.

Result #3

array.indexOf { $0 == object } // 2

Here you are explicitly using the == operator you defined so the value property is used.

Result #4

array.filter { $0 == object } // [{NSObject, value 5}]

Again the == operator is used and then the value property is used to check for equality.

Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148