* [NSMutableSet addObject:X] *添加对象X,即使* [Y isEqual:X] *对于集合中已有的对象Y返回TRUE

时间:2016-07-30 20:40:05

标签: objective-c nsmutableset

我有一个名为FactorHelper的Objective-C类,其定义如下。它有一个名为因子的属性,NSMutableArrayNSNumbers。我在这个类中有一个自定义isEqual:方法,如果两个FactorHelper对象中的因子属性具有相同的数字(即使数字的顺序不同),则返回true。

我尝试通过创建两个FactorHelper对象一个10,5,2 另一个10,2,5 进行测试。然后我创建了一个NSMutableSet,添加了firstObject,然后添加了第二个对象。我期待第二个对象不被添加,但我看到它被添加了。当我单步执行代码时,我发现addObject正在调用isEqual并返回TRUE。我做错了什么?

更新

[NSMutableSet new]更改为[NSMutableSet alloc] init]会使事情按预期进行。

此外,更改全部为TRUE,FALSE为isEqual为YES,NO使其行为正确(即使我将其保持为[NSMutableSet new])。

我不知道发生了什么事。有人可以解释一下吗?!

课程定义

@interface FactorHelper: NSObject
 @property NSMutableArray <NSNumber *> *factors;
 -(BOOL) isEqual:(FactorHelper *)other;
 -(instancetype) initWithFactors:(NSMutableArray *)factors;
 -(NSString *) description;
@end

@implementation FactorHelper

- (instancetype) initWithFactors:(NSMutableArray *)factors
{
    self = [super init];

    if (self) {
        _factors = factors;
    }

    return self;
}

-(BOOL) isEqual:(FactorHelper *)other
{
    if ([self.factors count] != [other.factors count])
    {
        return FALSE;

    }
    else
    {
        NSMutableDictionary <NSNumber *, NSNumber *> *myHashTable = [[NSMutableDictionary alloc] init];
        for (NSNumber *nextNumber in self.factors) {
            if(myHashTable[nextNumber] == nil)
            {
                myHashTable[nextNumber] = @(1);
            }
            else
            {
                myHashTable[nextNumber] = @([myHashTable[nextNumber] integerValue]+1);
            }
        }

        for (NSNumber *nextNumber in other.factors)
        {
            if(myHashTable[nextNumber] == nil)
            {
                return FALSE;
            }
            else
            {
                myHashTable[nextNumber] = @([myHashTable[nextNumber] integerValue] - 1);

                if ([myHashTable[nextNumber] integerValue] == 0) {
                    [myHashTable removeObjectForKey:nextNumber];
                }
            }
        }

        if ([[myHashTable allKeys] count] == 0)
        {
            return TRUE;
        }
        else
        {
            return FALSE;
        }

    }
}
@end

单元测试代码

NSMutableSet *testSet = [NSMutableSet new];
FactorHelper *fact1 = [[FactorHelper alloc] initWithFactors:[@[@(10),@(5),@(2)] mutableCopy]];
FactorHelper *fact2 = [[FactorHelper alloc] initWithFactors:[@[@(10),@(2),@(5)] mutableCopy]];
[testSet addObject:fact1];
[testSet addObject:fact2];
NSLog(@"Are factors 1 and 2 the same: %d",[fact1 isEqual:fact2]);

2 个答案:

答案 0 :(得分:2)

NSMutableSet是基于哈希值的集合。您需要覆盖与hash一致的元素类型的isEqual:方法。

在你的情况下,像这样:

- (NSUInteger)hash {
    NSCountedSet *factorCounts = [[NSCountedSet alloc] initWithArray:self.factors];
    return [@"FactorHelper" hash] + [factorCounts hash];
}

我不确定您是否检查了我是否看到它已添加,但这会使FactorHelperNSMutableSet一起使用。

顺便说一下,使用isEqual:可以缩短您的NSCountedSet

-(BOOL) isEqual:(FactorHelper *)other {
    NSCountedSet *myFactorCounts = [[NSCountedSet alloc] initWithArray:self.factors];
    NSCountedSet *otherFactorCounts = [[NSCountedSet alloc] initWithArray:other.factors];
    return [myFactorCounts isEqual:otherFactorCounts];
}

这表明与上面的hash更加清晰。

答案 1 :(得分:1)

你的代码从不正在工作,即使它有时会出现。

问题是isEqual的自定义实现不是使类在Set中工作的唯一要求。想一想: 是什么?它是哈希表。因此,您还必须提供匹配的hash自定义实现 - 您还没有这样做。

对可持续性的要求是:如果这两个对象相等,则两个对象的哈希值必须相同。