Se non è possibile ottenere un oggetto con objectAtIndex: da un NSSet, come si recuperano gli oggetti?
Se non è possibile ottenere un oggetto con objectAtIndex: da un NSSet, come si recuperano gli oggetti?
Risposte:
Esistono diversi casi d'uso per un set. È possibile enumerare tramite (ad esempio con enumerateObjectsUsingBlock
o NSFastEnumeration), chiamare containsObject
per verificare l'appartenenza, utilizzare anyObject
per ottenere un membro (non casuale) o convertirlo in un array (in nessun ordine particolare) con allObjects
.
Un set è appropriato quando non vuoi duplicati, non ti interessa l'ordine e vuoi testare l'appartenenza veloce.
member:
messaggio al set . Se ritorna nil
, l'insieme non contiene un oggetto uguale a quello passato; se restituisce un puntatore a un oggetto, il puntatore che restituisce è all'oggetto già presente nell'insieme. Gli oggetti nell'insieme devono implementare hash
e isEqual:
affinché ciò sia utile.
hash
le esigenze da attuare; sarebbe solo molto più veloce se lo facessi.
hash
nel protocollo NSObject: "Se due oggetti sono uguali (come determinato dal isEqual:
metodo), devono avere lo stesso valore hash." Attualmente, le implementazioni di NSObject hash
e isEqual:
utilizzano l'identità dell'oggetto (indirizzo). Se esegui l'override isEqual:
, stai impostando la possibilità di oggetti che non sono identici ma sono uguali, che, se non esegui anche l'override hash
, avranno comunque hash diversi. Ciò viola il requisito che gli oggetti uguali abbiano hash uguali.
member:
, il contenitore cercherà solo in quello bucket (motivo per cui gli insiemi sono molto più veloci degli array durante il test di appartenenza e i dizionari sono molto più veloci degli array paralleli nella ricerca di valori-chiave). Se l'oggetto cercato ha l'hash sbagliato, il contenitore cercherà nel bucket sbagliato e non troverà una corrispondenza.
-[NSObject hash]
fosse 0. Questo spiega molto. = S
NSSet non ha un metodo objectAtIndex:
Prova a chiamare allObjects che restituisce un NSArray di tutti gli oggetti.
è possibile utilizzare filteredSetUsingPredicate se si dispone di un qualche tipo di identificatore univoco per selezionare l'oggetto di cui si ha bisogno.
Per prima cosa crea il predicato (supponendo che il tuo ID univoco nell'oggetto sia chiamato "identificatore" ed è un NSString):
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:@"identifier == %@", identifier];
Quindi scegli l'oggetto utilizzando il predicato:
NSObject *myChosenObject = [mySet filteredSetUsingPredicate:myPredicate].anyObject;
Per Swift3 e iOS10:
//your current set
let mySet : NSSet
//targetted index
let index : Int
//get object in set at index
let object = mySet.allObjects[index]
NSSet utilizza il metodo isEqual: (che gli oggetti che inserisci in quel set devono sovrascrivere, inoltre, il metodo hash) per determinare se un oggetto è al suo interno.
Quindi, ad esempio, se hai un modello di dati che definisce la sua unicità da un valore id (supponiamo che la proprietà sia:
@property NSUInteger objectID;
quindi implementeresti isEqual: as
- (BOOL)isEqual:(id)object
{
return (self.objectID == [object objectID]);
}
e potresti implementare 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
}
Quindi, puoi ottenere un oggetto con quell'ID (oltre a verificare se è in 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
Ma sì, l'unico modo per ottenere qualcosa da un NSSet è considerare ciò che definisce la sua unicità in primo luogo.
Non ho testato cosa è più veloce, ma evito di usare l'enumerazione perché potrebbe essere lineare mentre l'uso del metodo member: sarebbe molto più veloce. Questo è uno dei motivi per preferire l'uso di NSSet invece di NSArray.
for (id currentElement in mySet)
{
// ** some actions with currentElement
}
Il più delle volte non ti interessa prendere un oggetto particolare da un set. Ti interessa testare per vedere se un set contiene un oggetto. Ecco a cosa servono i set. Quando vuoi vedere se un oggetto è in una raccolta, i set sono molto più veloci degli array.
Se non ti interessa quale oggetto ottieni, usa quello -anyObject
che ti dà solo un oggetto dal set, come mettere la mano in una borsa e afferrare qualcosa.
Dog *aDog = [dogs anyObject]; // dogs is an NSSet of Dog objects
Se ti interessa quale oggetto ottieni, usa -member
che ti restituisce l'oggetto o nullo se non è nel set. Devi avere già l'oggetto prima di chiamarlo.
Dog *spot = [Dog dogWithName:@"Spot"];
// ...
Dog *aDog = [dogs member:spot]; // Returns the same object as above
Ecco del codice che puoi eseguire in Xcode per saperne di più
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]