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

时间:2012-02-20 01:42:06

标签: objective-c cocoa 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不完全相同。这似乎解决了错误。当对象在逻辑上相同时,这是否指向某种编译器优化?

2 个答案:

答案 0 :(得分:10)

  

我假设setWithArray正在进行基本地址比较

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

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

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

  

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

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

答案 1 :(得分:3)

您很可能没有为hash实施Person,有时相同的Person对象会哈希到两个不同的存储桶中。