Why does containsObject not work on an NSSet copy of NSMutableSet?

202 Views Asked by At

I’m having an issue when creating a set copy of a mutable set, and then having containsObject fail for the (new) set copy. For example:

NSString *sameID = @"XYZ";

Then on main thread/sometimes on different thread:

[mutableSet addObject:sameID];
NSLog(@"adding Object: %@", sameID);

Then on a background thread, always after (I've verified with logging that) sameID has been added:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    NSSet<NSString *> *newSet = self.mutableSet.copy;
    if ([newSet containsObject:sameID]) {
      // do stuff
    }
});

Seen with a breakpoint:

1. po [newSet containsObject:sameID] // returns NO
2. po [self.mutableSet containsObject:sameID] // returns YES

I've tested that there is no additional or hidden whitespace for the sameID string. AFAIK containsObject for sets containing NSString uses isEqual which doesn’t/shouldn't matter if the strings have different pointers, right?

Edit: Some additional information: the NSStrings that are being added to the mutableSet are happening on different threads but always before creating the newSet copy. Creating and checking newSet is being done on a background thread which is why I'm copying mutableSet (and I'm seeing with logging that this is always after having added string(s) to mutableSet). So I see with log statements that the string (sameID) was added to mutableSet, then on a background thread I'm copying mutableSet to be a set and quickly checking that set for the same string (sameID) and it's not there.

Edit2: I've tested the code below and it passes for me, however when I tried implementing the same solution regarding above it still has the same result (mutableSet contains the string yet newSet does not).

import XCTest
import Foundation

class CustomObj: NSObject {
    let id: String

    init(id: String) { self.id = id }

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

    override var hash: Int {
        return id.hashValue
    }
}

func testSetStrings() {
    let set = NSMutableSet(object: CustomObj(id: "String-1"))
    let copySet: Set<CustomObj> = set.copy() as! Set<CustomObj>

    let customObjForTest = CustomObj(id: "String-1")

    XCTAssertTrue(set.contains(customObjForTest), "Does Not Contain")
    XCTAssertTrue(copySet.contains(customObjForTest), "Copy Does Not Contain")
}

testSetStrings()
0

There are 0 best solutions below