*[NSMutableSet addObject: X]* 添加对象 X,即使 *[Y isEqual:X]* returns TRUE 对于已经在集合中的对象 Y

*[NSMutableSet addObject: X]* adds object X even though *[Y isEqual:X]* returns TRUE for an object Y already in the Set

我有一个名为 FactorHelper 的 Objective-C class,其定义如下。它有一个 属性 称为因子,它是 NSNumbersNSMutableArray。我在此 class 中有一个自定义 isEqual: 方法,如果两个 FactorHelper 对象中的因子 属性 具有相同的数字(即使数字顺序不同)。

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

更新

[NSMutableSet new] 更改为 [NSMutableSet alloc] init] 让事情按预期工作。

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

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

Class定义

@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]);

您的代码从未工作,即使有时看起来如此。

问题是 isEqual 的自定义实现并不是使 class 在 Set 中工作的唯一要求。想一想:什么一个集合?它是一个 散列 table。因此,您还必须提供 hash 的匹配自定义实现 — 而您还没有这样做。

散列性的要求是:如果两个对象相等,则两个对象的散列值必须相同。

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 更加一致。