Si no puede obtener un objeto con objectAtIndex: de un NSSet, ¿cómo recupera los objetos?
Si no puede obtener un objeto con objectAtIndex: de un NSSet, ¿cómo recupera los objetos?
Respuestas:
Hay varios casos de uso para un conjunto. Puede enumerar (por ejemplo, con enumerateObjectsUsingBlock
o NSFastEnumeration), llamar containsObject
para probar la membresía, usar anyObject
para obtener un miembro (no aleatorio) o convertirlo en una matriz (sin ningún orden en particular) con allObjects
.
Un conjunto es apropiado cuando no desea duplicados, no le importa el orden y desea pruebas rápidas de membresía.
member:
mensaje al conjunto . Si regresa nil
, el conjunto no contiene un objeto igual al que pasó; si devuelve un puntero de objeto, entonces el puntero que devuelve es al objeto que ya está en el conjunto. Los objetos del conjunto deben implementar hash
y isEqual:
para que esto sea útil.
hash
las necesidades que deban aplicarse; iría mucho más rápido si lo hicieras.
hash
protocolo NSObject: "Si dos objetos son iguales (según lo determinado por el isEqual:
método), deben tener el mismo valor hash". Actualmente, las implementaciones de NSObject hash
y isEqual:
utilizan la identidad (dirección) del objeto. Si anula isEqual:
, está configurando la posibilidad de objetos que no son idénticos pero sí iguales, que, si no anula también hash
, seguirán teniendo valores hash diferentes. Eso viola el requisito de que los objetos iguales tengan valores hash iguales.
member:
, el contenedor solo buscará en ese bucket (razón por la cual los conjuntos son mucho más rápidos que las matrices en las pruebas de membresía y los diccionarios son mucho más rápidos que las matrices paralelas en la búsqueda de valores clave). Si el objeto que se busca tiene el hash incorrecto, el contenedor buscará en el cubo incorrecto y no encontrará una coincidencia.
-[NSObject hash]
era 0. Eso explica muchas cosas. = S
NSSet no tiene un método objectAtIndex:
Intente llamar a allObjects que devuelve un NSArray de todos los objetos.
Es posible usar filterSetUsingPredicate si tiene algún tipo de identificador único para seleccionar el objeto que necesita.
Primero cree el predicado (asumiendo que su identificación única en el objeto se llama "identificador" y es un NSString):
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:@"identifier == %@", identifier];
Y luego elija el objeto usando el predicado:
NSObject *myChosenObject = [mySet filteredSetUsingPredicate:myPredicate].anyObject;
Para Swift3 y iOS10:
//your current set
let mySet : NSSet
//targetted index
let index : Int
//get object in set at index
let object = mySet.allObjects[index]
NSSet usa el método isEqual: (que los objetos que coloque en ese conjunto deben anular, además, el método hash) para determinar si un objeto está dentro de él.
Entonces, por ejemplo, si tiene un modelo de datos que define su singularidad mediante un valor de id (digamos que la propiedad es:
@property NSUInteger objectID;
entonces implementaría isEqual: as
- (BOOL)isEqual:(id)object
{
return (self.objectID == [object objectID]);
}
y podrías implementar hash:
- (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
}
Luego, puede obtener un objeto con esa ID (así como verificar si está en NSSet) con:
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
Pero sí, la única forma de obtener algo de un NSSet es considerando lo que define su singularidad en primer lugar.
No he probado qué es más rápido, pero evito usar la enumeración porque podría ser lineal, mientras que usar el método member: sería mucho más rápido. Esa es una de las razones para preferir el uso de NSSet en lugar de NSArray.
for (id currentElement in mySet)
{
// ** some actions with currentElement
}
La mayoría de las veces no le importa obtener un objeto en particular de un conjunto. Le interesa probar para ver si un conjunto contiene un objeto. Para eso sirven los sets. Cuando desea ver si un objeto está en una colección, los conjuntos son mucho más rápidos que las matrices.
Si no le importa el objeto que obtenga, use el -anyObject
que solo le dé un objeto del conjunto, como poner la mano en una bolsa y agarrar algo.
Dog *aDog = [dogs anyObject]; // dogs is an NSSet of Dog objects
Si le importa el objeto que obtiene, use el -member
que le devuelva el objeto, o nil si no está en el conjunto. Necesita tener el objeto antes de llamarlo.
Dog *spot = [Dog dogWithName:@"Spot"];
// ...
Dog *aDog = [dogs member:spot]; // Returns the same object as above
Aquí hay un código que puede ejecutar en Xcode para comprender más
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);
}
[[nsSetObjects allObjects] objectAtIndex: anyInteger]