2016-07-30 3 views
0

Ich habe eine Objective-C-Klasse namens FactorHelper deren Definition unten ist. Es hat eine Eigenschaft genannt Faktoren, die eine NSMutableArray von NSNumbers ist. Ich habe eine benutzerdefinierte isEqual: Methode in dieser Klasse, die wahr zurückgibt, wenn die Faktoren-Eigenschaft in den beiden FactorHelper Objekte die gleichen Nummern haben (auch wenn die Zahlen in einer anderen Reihenfolge sind).* [NSMutableSet addObject: X] * fügt Objekt X hinzu, obwohl * [Y ist gleich: X] * gibt TRUE für ein Objekt Y zurück, das bereits im Set ist

ich versuchte, durch die Schaffung von zwei Objekten FactorHelpereines mit 10,5,2und das andere mit 10,2,5 zu testen. Dann habe ich ein NSMutableSet erstellt, ErstesObjekt und dann zweites Objekt hinzugefügt. Ich habe erwartet, dass das zweite Objekt nicht hinzugefügt wird, aber ich sehe, dass es hinzugefügt wird. Wenn ich durch den Code gehe, finde ich, dass von addObject aufgerufen wird und TRUE zurückgibt. Was mache ich falsch?

UPDATE

Ändern der [NSMutableSet new] zu [NSMutableSet alloc] init] macht die Dinge wie erwartet.

Auch das Ändern aller TRUE, FALSE ist isEqual zu YES, NO bewirkt, dass es sich korrekt verhält (auch wenn ich es als [NSMutableSet new] halte).

Ich habe keine Ahnung was los ist. Kann jemand bitte etwas Licht abwerfen ?!

Klassendefinition

@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 

Unit-Test-Code

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]); 
+0

Wo in Ihrem Code verwenden Sie 'NSMutableSet'? – Willeke

+0

Das testSet ist ein NSMutableSet. Hinzugefügt im Hauptcode oben. Der Code gehört zur Hauptmethode eines OS X-Befehlszeilentools. Siehe meine Antwort unten. Wenn Sie alle WAHR/FALSCH auf JA/NEIN setzen, wird das Problem behoben. –

Antwort

2

NSMutableSet ist ein auf Hashwerten basierender Satz. Sie müssen die Methode hash für ihre Elementtypen überschreiben, die mit isEqual: konsistent sind.

In Ihrem Fall so etwas wie diese:

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

Ich bin nicht sicher, wie Sie, wenn geprüft ich sehe, dass es hinzugefügt wird, aber das macht Ihre FactorHelper Arbeit mit NSMutableSet.

Übrigens, Ihre isEqual: kann unter Verwendung von NSCountedSet etwas kürzer implementiert werden.

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

Dies zeigt klarere Konsistenz mit der hash oben.

+0

Danke @OOPer. Eine Frage: Haben alle Apple Collection-Klassen eingebauten "Hash"? Zum Beispiel wenn ich eine benutzerdefinierte Klasse, CLassX, habe, die zwei Eigenschaften hat, ein 'NSArray * -Array' und ein' NSIntegerVale'. Und ich möchte sagen, dass zwei Objekte von CLassX gleich sind, solange sie Elemente in Array und Wert haben. Setze ich einfach Hash auf [self.array hash] + [self.value hash]? –

+0

@SmartHome, soweit ich weiß, geben alle Collection-Typen Hash-Werte zurück, die mit ihrer 'isEqual:' Methode konsistent sind. Ich meine, wenn '[col1 isEqual: col2]' TRUE zurückgibt, '[col1 hash] == [col2 hash]' wird immer TRUE. (Dies ist die Voraussetzung dafür, dass 'hash' mit' isEqual: 'konsistent ist.) Um' hash' mit Gleichheit für ('NSArray * array',' NSInteger value') zu implementieren, '[self.array hash] + (NSUInteger) self.value' wird funktionieren. (Es kann bessere Implementierungen geben, aber das ist nicht so schlimm.) – OOPer

+0

@SmartHome, vielleicht muss ich beachten, dass "konsistent nicht effizient bedeutet". Um eine bessere Leistung mit Hash-basierten Sammlungen, wie zum Beispiel "NSDictionary" oder "NSSet", zu erhalten, ist die Minimierung der Möglichkeit von "[col1 hash] == [col2 hash]', aber nicht '[col1 isEqual: col2]' "erforderlich . Standard-Implementierungen von "Hash" in Apples Frameworks sind bei weitem nicht optimal für diesen Zweck optimiert. – OOPer

1

Der Code war nie arbeiten, auch wenn es manchmal erschien.

Das Problem ist, dass eine benutzerdefinierte Implementierung von isEqual ist nicht die einzige Voraussetzung für eine Klasse in einem Set arbeiten. Denken Sie: Was ist ein Set? Es ist eine Hash-Tabelle. Sie müssen also auch eine passende benutzerdefinierte Implementierung von hash bereitstellen - und Sie haben das nicht getan.

Voraussetzung für die Hashfähigkeit ist: Der Hash-Wert zweier Objekte muss gleich sein, wenn diese beiden Objekte gleich sind.

Verwandte Themen