将NSArray转换为NSSet时,自定义类实例传输不一致



遇到了一个有趣的小问题。我正在写一个方法来过滤一个数组到唯一的对象:

- (NSArray*)distinctObjectsByAddress {
    NSSet* uniqueSet = [NSSet setWithArray:self];
    NSArray* retArray = [uniqueSet allObjects];
    return retArray;
}

并编写了一个单元测试来检查:

- (void)testDistinctObjectsByAddress5 {
    Person* adam1 = [[Person alloc] initWithFirstName:@"adam" lastName:@"adam" andParent:nil];
    Person* adam2 = [[Person alloc] initWithFirstName:@"adam" lastName:@"adam" andParent:nil];
    testPersonArray = [NSArray arrayWithObjects:adam1,adam2, nil];
    NSArray* checkArray = [testPersonArray distinctObjectsByAddress];
    STAssertEquals([checkArray count], [testPersonArray count], @"Array %@ counts should match %@ %@",checkArray,adam1,adam2);
}

很简单。有趣的部分是,大约80-90%的时间测试通过,并且由于distinctObjectsByAddress方法只返回一个对象,所以它经常失败。我已经能够追踪到[NSSet setWithArray:self]调用,但我也能够验证两个人对象是两个不同的对象(至少他们有不同的地址)。我假设setWithArray:只是在做一个基本的地址比较,但我不明白为什么它有时会产生两个应该产生的对象,有时只产生一个。

我刚刚尝试过更改adam2,使其名字和姓氏与adam1不完全相同。这似乎修复了错误。当对象在逻辑上相同时,这是否指向某种编译器优化?

我假设setWithArray只是在做一个基本的地址比较

这是不正确的。NSSet在添加到它的对象上使用-isEqual:-hash方法。这取决于这些方法在Person或其超类中的实现方式。

如果是[person1 isEqual:person2],那么您希望该集合包含一个对象。如果不是,则该集合应包含两个对象。

我的猜测是Person没有遵循其-isEqual:-hash方法中的规则。最有可能的是,这两个对象是相等的,但它们的哈希值并不像应该的那样相等。(除了10-20%的时间你很幸运。)

当对象在逻辑上相同时,这是否指向某种编译器优化?

不,没有编译器优化可以将两个对象合并为一个。

很可能您没有为Person实现hash,有时相同的Person对象会散列到两个不同的桶中。

最新更新