6

Why can't the === be used with String's in Swift? I am unable to compile the following:

let string1 = "Bob"
let string2 = "Fred"

if string1 === string2 {
    ...
}

and get the following error (on the if line):

Binary operator '===' cannot be applied to two 'String' operands


What I want to be able to do in my unit tests is, having performed a copyWithZone:, verify that two objects are indeed a different object with different pointers even if their values are the same. The following code doesn't work...

XCTAssertFalse(object1.someString === object2.someString)

If anyone knows of an alternative way please advise.

nhgrif
  • 61,578
  • 25
  • 134
  • 173
Oliver Pearmain
  • 19,885
  • 13
  • 86
  • 90
  • It would be helpful if you provide (a minimal example of) your actual code: the class with the copyWithZone method and the unit test. – Martin R Jan 27 '16 at 14:20

5 Answers5

8

string1 and string2 are not NSString, but String. Since they are value objects, not reference objects, there is no reference that could be compared with ===.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
5

Swift's === operator, by default, is only defined for classes.

Swift's String type is not a class but a struct. It does not inherit from AnyObject and therefore cannot be compared by reference.

You could of course implement an === operator for String in Swift, but I'm not sure how it would be any different from the implementation of == for Swift's String type.

func ===(lhs: String, rhs: String) -> Bool {
    return lhs == rhs
}

Unless, of course, you really wanted to compare the references, I suppose you could do something like this:

func ===(lhs: String, rhs: String) -> Bool {
    return unsafeAddressOf(lhs) == unsafeAddressOf(rhs)
}

However, for the sake of tests, rather than using the == or === operators, you should use the appropriate assertions:

XCTAssertEqual(foo, bar)
XCTAssertNotEqual(foo, bar)
Mogsdad
  • 44,709
  • 21
  • 151
  • 275
nhgrif
  • 61,578
  • 25
  • 134
  • 173
  • They're struct's in Swift, who knew! Thank you for attempting to address my testing issue however I think my problems are bigger than this because I am trying to test in Swift a class that is defined in Objective-C so the class does use NSString objects in real life but in my tests its converted to Swift String primatives. – Oliver Pearmain Jan 27 '16 at 13:57
  • unsafeAddressOf() is pretty useless with Swift Strings, compare http://stackoverflow.com/questions/32638879/swift-strings-and-memory-addresses. – Martin R Jan 27 '16 at 14:03
  • Is it valid to imply that Swift classes inherit from `AnyObject`? You can cast any class to an AnyObject, but I'm not sure it's right to say that they inherit from it. Or is this wrong? http://stackoverflow.com/questions/24137368/why-is-there-no-universal-base-class-in-swift – Grimxn Jan 27 '16 at 14:13
  • @Grimxn: `String` is not a class, but a struct. *Some* types (String, Int, Float, ...) are automatically bridged to Foundation classes (NSString, NSNumber, ...) if Foundation is imported. – Martin R Jan 27 '16 at 14:17
  • Martin, I know that, but @nhgrif implies in his answer that Swift *classes* inherit from `AnyObject`. Second paragraph, second sentence. – Grimxn Jan 27 '16 at 14:31
  • AnyObject is a *protocol* to which all classes implicitly conform. – Martin R Jan 27 '16 at 14:43
3

The === operator is the identity operator. It checks if two variables or constants refer to the same instance of a class. Strings are not classes (they are structs) so the === operator does not apply to them.

If you want to check if two strings are the same, use the equality operator == instead.

Read all about the identity operator in the Swift documentation.

You can just check two objects for identity directly, instead of checking a property of type String.

 XCTAssertFalse(object1 === object2)
Marc Khadpe
  • 2,012
  • 16
  • 14
2

Swift Strings are value type, not reference type, so there's no need for that, a copy will always be a different object.

You should just compare by value with ==.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
0

If you try really hard, you can force things to happen, but I'm not sure what that buys you.

class MyClass: NSObject, NSCopying {
    var someString: NSString = ""

    required override init() {
        super.init()
    }

    func copyWithZone(zone: NSZone) -> AnyObject {
        let copy = self.dynamicType.init()
        copy.someString = someString.copy() as? NSString ?? ""
        return copy
    }
}

let object1 = MyClass()
object1.someString = NSString(format: "%d", arc4random())
let object2 = object1.copy()

if object1.someString === object2.someString {
    print("identical")
} else {
    print("different")
}

prints identical, the system is really good at conserving strings.

Jeffery Thomas
  • 42,202
  • 8
  • 92
  • 117