Obtenir un objet à partir d'un NSSet


Réponses:


139

Il existe plusieurs cas d'utilisation pour un ensemble. Vous pouvez énumérer via (par exemple avec enumerateObjectsUsingBlockou NSFastEnumeration), appeler containsObjectpour tester l'appartenance, utiliser anyObjectpour obtenir un membre (non aléatoire) ou le convertir en un tableau (sans ordre particulier) avec allObjects.

Un ensemble est approprié lorsque vous ne voulez pas de doublons, que vous ne vous souciez pas de la commande et que vous voulez des tests d'adhésion rapides.


7
Vous pouvez également rechercher un objet connu par un doublon potentiel en envoyant un member:message à l'ensemble . S'il retourne nil, l'ensemble ne contient pas un objet égal à celui que vous avez passé; s'il renvoie un pointeur d'objet, alors le pointeur qu'il renvoie est vers l'objet déjà dans l'ensemble. Les objets de l'ensemble doivent implémenter hashet isEqual:pour que cela soit utile.
Peter Hosey

@PeterHosey Je ne pense pas qu'il soit hash nécessaire de l'implémenter; cela irait beaucoup plus vite si vous faisiez cela.
fumoboy007

2
@ fumoboy007: Pour stocker dans un ensemble ou utiliser comme clé dans un dictionnaire, oui. D'après la documentation de hashdans le protocole NSObject: «Si deux objets sont égaux (comme déterminé par la isEqual:méthode), ils doivent avoir la même valeur de hachage.» Actuellement, les implémentations de NSObject hashet isEqual:utilisent l'identité (adresse) de l'objet. Si vous remplacez isEqual:, vous configurez la possibilité d'objets qui ne sont pas identiques mais égaux - qui, si vous ne remplacez pas également hash, auront toujours des hachages différents. Cela viole l'exigence selon laquelle les objets égaux ont des hachages égaux.
Peter Hosey

1
@ fumoboy007: considérez le fonctionnement d'une table de hachage: le conteneur a un tableau d'un certain nombre de compartiments et utilise le hachage de chaque objet entrant pour déterminer dans quel compartiment cet objet doit se trouver. Pour les recherches telles que member:, le conteneur ne recherchera que dans celui-là bucket (c'est pourquoi les ensembles sont tellement plus rapides que les tableaux lors des tests d'appartenance et les dictionnaires sont tellement plus rapides que les tableaux parallèles lors de la recherche clé-valeur). Si l'objet recherché a le mauvais hachage, le conteneur recherchera dans le mauvais compartiment et ne trouvera pas de correspondance.
Peter Hosey

@PeterHosey Oups… J'ai pensé à tort que l'implémentation par défaut de -[NSObject hash]était 0. Cela explique beaucoup de choses. = S
fumoboy007

31

NSSet n'a pas de méthode objectAtIndex:

Essayez d'appeler allObjects qui renvoie un NSArray de tous les objets.


avez-vous une idée si le tableau retourné est ordonné? en d'autres termes, si j'ajoute des objets à l'ensemble en utilisant "setByAddingObject", et que j'ai utilisé "allObjects", le sont-ils? les éléments du tableau classés dans l'ordre où j'ai ajouté les objets?
iosMentalist

il suffit de trier le tableau résultant avec un NSSortPredicate et tout ira bien
Jason

Si vous n'êtes intéressé que par un objet spécifique, utilisez mieux l'approche filteredSetUsingPredicate.
akw

17

il est possible d'utiliser filteredSetUsingPredicate si vous avez une sorte d'identifiant unique pour sélectionner l'objet dont vous avez besoin.

Commencez par créer le prédicat (en supposant que votre identifiant unique dans l'objet s'appelle "identifiant" et qu'il s'agit d'une NSString):

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

Et puis choisissez l'objet en utilisant le prédicat:

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

13

NSArray *myArray = [myNSSet allObjects];

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

remplacez NSUInteger par l'index de l'objet souhaité.


1
Doit être: MyObject * object = [myArray objectAtIndex: (NSUInteger *)]
John D.

8

Pour Swift3 et 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 utilise la méthode isEqual: (que les objets que vous placez dans cet ensemble doivent remplacer, en outre, la méthode de hachage) pour déterminer si un objet se trouve à l'intérieur de celui-ci.

Ainsi, par exemple, si vous avez un modèle de données qui définit son unicité par une valeur d'id (disons que la propriété est:

@property NSUInteger objectID;

alors vous implémenteriez isEqual: as

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

et vous pouvez implémenter le hachage:

- (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
}

Ensuite, vous pouvez obtenir un objet avec cet ID (ainsi que vérifier s'il est dans le NSSet) avec:

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

Mais oui, la seule façon d'obtenir quelque chose d'un NSSet est de considérer ce qui définit son unicité en premier lieu.

Je n'ai pas testé ce qui est le plus rapide, mais j'évite d'utiliser l'énumération car cela pourrait être linéaire alors que l'utilisation de la méthode member: serait beaucoup plus rapide. C'est l'une des raisons de préférer l'utilisation de NSSet au lieu de NSArray.



0

La plupart du temps, vous ne vous souciez pas d'obtenir un objet particulier d'un ensemble. Vous vous souciez de tester pour voir si un ensemble contient un objet. C'est à cela que servent les décors. Lorsque vous voulez voir si un objet est dans une collection, les ensembles sont beaucoup plus rapides que les tableaux.

Si vous ne vous souciez pas de ce qui obtenez vous opposez, utilisez ce -anyObjectqui vous donne un seul objet de l'ensemble, comme mettre la main dans un sac et saisissant quelque chose.

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

Si vous vous souciez de l'objet que vous obtenez, utilisez celui -memberqui vous rend l'objet, ou nul s'il n'est pas dans l'ensemble. Vous devez déjà disposer de l'objet avant de l'appeler.

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

Voici un code que vous pouvez exécuter dans Xcode pour en savoir plus

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);
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.