Abrufen eines Objekts von einem NSSet


Antworten:


139

Es gibt mehrere Anwendungsfälle für einen Satz. Sie können mit (z. B. mit enumerateObjectsUsingBlockoder NSFastEnumeration) auflisten, aufrufen containsObject, um die Mitgliedschaft zu testen anyObject, ein Mitglied (nicht zufällig) abrufen oder es mit in ein Array (in keiner bestimmten Reihenfolge) konvertieren allObjects.

Ein Set ist geeignet, wenn Sie keine Duplikate möchten, sich nicht um die Reihenfolge kümmern und schnelle Mitgliedschaftstests wünschen.


7
Sie können ein bekanntes Objekt auch anhand eines potenziellen Duplikats nachschlagen, indem Sie dem Set eine member:Nachricht senden . Wenn es zurückkehrt nil, enthält die Menge kein Objekt, das dem Objekt entspricht, das Sie übergeben haben. Wenn ein Objektzeiger zurückgegeben wird, bezieht sich der zurückgegebene Zeiger auf das Objekt, das sich bereits in der Menge befindet. Die Objekte in der Menge implementieren muss hashund isEqual:für diese nützlich sein.
Peter Hosey

@PeterHosey Ich glaube nicht , hash Bedürfnisse umgesetzt werden; es würde einfach viel schneller gehen, wenn du das tust.
Fumoboy007

2
@ fumoboy007: Für die Speicherung in einem Set oder als Schlüssel in einem Wörterbuch, ja, das tut es. Aus der Dokumentation hashim NSObject-Protokoll: „Wenn zwei Objekte gleich sind (wie von der isEqual:Methode bestimmt), müssen sie denselben Hashwert haben.“ Derzeit implementiert hashund isEqual:verwendet NSObject die Identität (Adresse) des Objekts. Wenn Sie überschreiben isEqual:, richten Sie die Möglichkeit von Objekten ein, die nicht identisch, aber gleich sind. Wenn Sie nicht auch überschreiben hash, werden immer noch unterschiedliche Hashes angezeigt. Dies verstößt gegen die Anforderung, dass gleiche Objekte gleiche Hashes haben.
Peter Hosey

1
@ fumoboy007: Überlegen Sie, wie eine Hash-Tabelle funktioniert: Der Container verfügt über ein Array mit einer bestimmten Anzahl von Buckets und verwendet den Hash jedes eingehenden Objekts, um zu bestimmen, in welchem ​​Bucket sich das Objekt befinden soll. Bei Suchvorgängen wie z. B. member:wird der Container nur in diesem angezeigt Bucket (weshalb Sets beim Testen der Mitgliedschaft so viel schneller sind als Arrays und Wörterbücher beim Nachschlagen von Schlüsselwerten so viel schneller als parallele Arrays). Wenn das gesuchte Objekt den falschen Hash hat, sucht der Container im falschen Eimer und findet keine Übereinstimmung.
Peter Hosey

@PeterHosey Ups ... Ich dachte fälschlicherweise, dass die Standardimplementierung von -[NSObject hash]0 war. Das erklärt viel. = S
fumoboy007

31

NSSet hat keine Methode objectAtIndex:

Versuchen Sie, allObjects aufzurufen, das ein NSArray aller Objekte zurückgibt.


Haben Sie eine Idee, ob das zurückgegebene Array bestellt ist? Mit anderen Worten, wenn ich Objekte mit "setByAddingObject" zum Set hinzufüge und "allObjects" verwende, sind das? die Elemente im Array in der Reihenfolge geordnet, in der ich die Objekte hinzugefügt habe?
iosMentalist

Sortieren Sie einfach das resultierende Array mit einem NSSortPredicate und es wird Ihnen gut gehen
Jason

Wenn Sie nur an einem bestimmten Objekt interessiert sind, verwenden Sie besser den FilteredSetUsingPredicate-Ansatz.
Akw

17

Es ist möglich, filteredSetUsingPredicate zu verwenden, wenn Sie eine eindeutige Kennung zur Auswahl des gewünschten Objekts haben.

Erstellen Sie zuerst das Prädikat (vorausgesetzt, Ihre eindeutige ID im Objekt heißt "Bezeichner" und ist ein NSString):

NSPredicate *myPredicate = [NSPredicate predicateWithFormat:@"identifier == %@", identifier];

Wählen Sie dann das Objekt mit dem Prädikat aus:

NSObject *myChosenObject = [mySet filteredSetUsingPredicate:myPredicate].anyObject;

13

NSArray *myArray = [myNSSet allObjects];

MyObject *object = [myArray objectAtIndex:(NSUInteger *)]

Ersetzen Sie NSUInteger durch den Index Ihres gewünschten Objekts.


1
Sollte sein: MyObject * object = [myArray objectAtIndex: (NSUInteger *)]
John D.

8

Für Swift3 und iOS10:

//your current set
let mySet : NSSet
//targetted index
let index : Int
//get object in set at index
let object = mySet.allObjects[index]

6

NSSet verwendet die Methode isEqual: (die Objekte, die Sie in diesen Satz einfügen, müssen zusätzlich die Hash-Methode überschreiben), um festzustellen, ob sich ein Objekt darin befindet.

Wenn Sie beispielsweise ein Datenmodell haben, das seine Eindeutigkeit durch einen ID-Wert definiert (sagen wir, die Eigenschaft lautet:

@property NSUInteger objectID;

dann würden Sie isEqual: as implementieren

- (BOOL)isEqual:(id)object
{
  return (self.objectID == [object objectID]);
}

und Sie könnten Hash implementieren:

- (NSUInteger)hash
{
 return self.objectID;  // to be honest, I just do what Apple tells me to here
                       // because I've forgotten how Sets are implemented under the hood
}

Anschließend können Sie ein Objekt mit dieser ID abrufen (und prüfen, ob es sich im NSSet befindet) mit:

MyObject *testObject = [[MyObject alloc] init];
testObject.objectID = 5; // for example.  

// I presume your object has more properties which you don't need to set here 
// because it's objectID that defines uniqueness (see isEqual: above)

MyObject *existingObject = [mySet member: testObject];

// now you've either got it or existingObject is nil

Aber ja, der einzige Weg, etwas aus einem NSSet herauszuholen, besteht darin, das zu berücksichtigen, was seine Einzigartigkeit überhaupt definiert.

Ich habe nicht getestet, was schneller ist, aber ich vermeide die Verwendung von Aufzählungen, da dies linear sein könnte, während die Verwendung der member: -Methode viel schneller wäre. Dies ist einer der Gründe, die Verwendung von NSSet anstelle von NSArray zu bevorzugen.



0

Meistens ist es Ihnen egal, ob Sie ein bestimmtes Objekt aus einem Set abrufen. Sie möchten testen, ob ein Satz ein Objekt enthält. Dafür sind Sets gut. Wenn Sie sehen möchten, ob sich ein Objekt in einer Sammlung befindet, sind Sätze viel schneller als Arrays.

Wenn Sie sich nicht darum kümmern, welches Objekt Sie erhalten, verwenden Sie das, -anyObjectdas Ihnen nur ein Objekt aus dem Set gibt, z. B. Ihre Hand in eine Tasche stecken und etwas greifen.

Dog *aDog = [dogs anyObject]; // dogs is an NSSet of Dog objects

Wenn Sie sich für das Objekt interessieren, das Sie erhalten, verwenden -memberSie, um das Objekt zurückzugeben, oder null, wenn es nicht im Set enthalten ist. Sie müssen das Objekt bereits haben, bevor Sie es aufrufen.

Dog *spot = [Dog dogWithName:@"Spot"];
// ...
Dog *aDog = [dogs member:spot]; // Returns the same object as above

Hier ist ein Code, den Sie in Xcode ausführen können, um mehr zu verstehen

NSString *one = @"One";
NSString *two = @"Two";
NSString *three = @"Three";

NSSet *set = [NSSet setWithObjects:one, two, three, nil];

// Can't use Objective-C literals to create a set.
// Incompatible pointer types initializing 'NSSet *' with an expression of type 'NSArray *'
//  NSSet *set = @[one, two, three];

NSLog(@"Set: %@", set);
// Prints looking just like an array but is actually not in any order
//Set: {(
//     One,
//     Two,
//     Three
//     )}

// Get a random object
NSString *random = [set anyObject];
NSLog(@"Random: %@", random); // Random: One

// Iterate through objects. Again, although it prints in order, the order is a lie
for (NSString *aString in set) {
    NSLog(@"A String: %@", aString);
}

// Get an array from the set
NSArray *array = [set allObjects];
NSLog(@"Array: %@", array);

// Check for an object
if ([set containsObject:two]) {
    NSLog(@"Set contains two");
}

// Check whether a set contains an object and return that object if it does (nil if not)
NSString *aTwo = [set member:two];
if (aTwo) {
    NSLog(@"Set contains: %@", aTwo);
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.