Ottenere un oggetto da un NSSet


Risposte:


139

Esistono diversi casi d'uso per un set. È possibile enumerare tramite (ad esempio con enumerateObjectsUsingBlocko NSFastEnumeration), chiamare containsObjectper verificare l'appartenenza, utilizzare anyObjectper 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.


7
Puoi anche cercare un oggetto noto da un potenziale duplicato inviando un 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 hashe isEqual:affinché ciò sia utile.
Peter Hosey

@PeterHosey Non credo che hash le esigenze da attuare; sarebbe solo molto più veloce se lo facessi.
fumoboy007

2
@ fumoboy007: Per l'archiviazione in un set o l'uso come chiave in un dizionario, sì, lo fa. Dalla documentazione di hashnel protocollo NSObject: "Se due oggetti sono uguali (come determinato dal isEqual:metodo), devono avere lo stesso valore hash." Attualmente, le implementazioni di NSObject hashe 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.
Peter Hosey

1
@ fumoboy007: Considera come funziona una tabella hash: il contenitore ha un array di un certo numero di bucket e utilizza l'hash di ogni oggetto in entrata per determinare in quale bucket dovrebbe trovarsi quell'oggetto. Per ricerche come 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.
Peter Hosey

@ PeterHosey Oops ... ho erroneamente pensato che l'implementazione predefinita di -[NSObject hash]fosse 0. Questo spiega molto. = S
fumoboy007

31

NSSet non ha un metodo objectAtIndex:

Prova a chiamare allObjects che restituisce un NSArray di tutti gli oggetti.


hai idea se l'array restituito è ordinato? in altre parole, se sto aggiungendo oggetti al set usando "setByAddingObject", e ho usato "allObjects", sono? gli elementi nell'array ordinati nell'ordine in cui ho aggiunto gli oggetti?
iosMentalist

basta ordinare l'array risultante con un NSSortPredicate e starai bene
Jason

Se sei interessato solo a un oggetto specifico, utilizza meglio l'approccio filteredSetUsingPredicate.
akw

17

è 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;

13

NSArray *myArray = [myNSSet allObjects];

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

sostituire NSUInteger con l'indice dell'oggetto desiderato.


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

8

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]

6

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.



0

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 -anyObjectche 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 -memberche 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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.