Re: -[NSSet containsObject:] returns NO when it should return YES
Re: -[NSSet containsObject:] returns NO when it should return YES
- Subject: Re: -[NSSet containsObject:] returns NO when it should return YES
- From: Andy Lee <email@hidden>
- Date: Sun, 20 Feb 2011 00:54:45 -0500
On Feb 19, 2011, at 10:31 PM, Ken Thomases wrote:
> On Feb 19, 2011, at 9:26 PM, Quincey Morris wrote:
>
>> On Feb 19, 2011, at 19:06, Kyle Sluder wrote:
>>
>>> Your experience indicates that -containsObject: uses pointer identity, whereas -member: uses -isEqual:.
>>
>> What's especially confusing is that -[NSArray containsObject:] is documented to use -isEqual. It seems like the NSSet method ought to be called -containsObjectIdenticalTo: for consistency (and now clarity) reasons.
>
> I think Kyle was just mistaken about this, lead astray by Michael's claims. Andy's experiment shows that -[NSSet containsObject:] relies on equality, not identity, just as one expects. It's just that Michael has two truly non-equal NSNumber objects, as his gdb output showed.
BTW I was technically mistaken about my code's output. You get the *same* instance when you do [NSNumber numberWithLongLong:1] twice, because NSNumber happens to cache instances for low integer values. But I was correct in principle: substitute 123456789 for 1 and everything I said becomes true.
One thing I'm puzzled about is that NSSet does not copy its entries the way NSDictionary copies its keys. This is implied by the member: documentation quoted earlier:
"If the set contains an object equal to object (as determined by isEqual:) then that object (***typically this will be object***), otherwise nil."
A quick experiment confirms that elements are not copied, which leads to the risk that NSDictionary avoids. If elements of the set happen to be mutable, one element could mutate in such a way as to be isEqual: to another. This can be demonstrated actually happening:
NSMutableString *s1 = [NSMutableString stringWithString:@"abc"];
NSMutableString *s2 = [NSMutableString stringWithString:@"abc"];
NSMutableString *s3 = [NSMutableString stringWithString:@"def"];
NSSet *stringSet = [NSSet setWithObjects:s1, s2, s3, nil];
NSLog(@"+++ s1 [%@] %p, hash = %ld, set contains s1? %d", s1, s1, [s1 hash], [stringSet containsObject:s1]);
NSLog(@"+++ s2 [%@] %p, hash = %ld, set contains s2? %d", s2, s2, [s2 hash], [stringSet containsObject:s2]);
NSLog(@"+++ s3 [%@] %p, hash = %ld, set contains s3? %d", s3, s3, [s3 hash], [stringSet containsObject:s3]);
NSLog(@"+++ elements BEFORE mutate:");
for (id obj in stringSet)
{
NSLog(@"+++ %p is [%@]", obj, obj);
}
[s3 setString:@"abc"]; // Mutate one set element to be equal to another.
NSLog(@"+++ count after mutate = %d", [stringSet count]);
NSLog(@"+++ elements AFTER mutate:");
for (id obj in stringSet)
{
NSLog(@"+++ %p is [%@] -- is eq to @\"abc\"? %d", obj, obj, [obj isEqual:@"abc"]);
}
Output:
2011-02-20 00:47:19.305 Scratcho[20333:a0f] +++ s1 [abc] 0x12b170, hash = 516202353, set contains s1? 1
2011-02-20 00:47:19.309 Scratcho[20333:a0f] +++ s2 [abc] 0x12b1b0, hash = 516202353, set contains s2? 1
2011-02-20 00:47:19.310 Scratcho[20333:a0f] +++ s3 [def] 0x12b1f0, hash = 517992642, set contains s3? 1
2011-02-20 00:47:19.310 Scratcho[20333:a0f] +++ elements BEFORE mutate:
2011-02-20 00:47:19.311 Scratcho[20333:a0f] +++ 0x12b170 is [abc]
2011-02-20 00:47:19.313 Scratcho[20333:a0f] +++ 0x12b1f0 is [def]
2011-02-20 00:47:19.313 Scratcho[20333:a0f] +++ count after mutate = 2
2011-02-20 00:47:19.314 Scratcho[20333:a0f] +++ elements AFTER mutate:
2011-02-20 00:47:19.315 Scratcho[20333:a0f] +++ 0x12b170 is [abc] -- is eq to @"abc"? 1
2011-02-20 00:47:19.315 Scratcho[20333:a0f] +++ 0x12b1f0 is [abc] -- is eq to @"abc"? 1
Am I missing something? Is this a known loophole in the semantics of NSSet? Is it a deliberate compromise, which trades the performance gained by not copying elements for a risk that almost never matters in practice?
--Andy
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden