Pobieranie obiektu z NSSet

94

Jeśli nie możesz pobrać obiektu z objectAtIndex: z NSSet, to w jaki sposób możesz odzyskać obiekty?

węzeł ninja
źródło
1
[[nsSetObjects allObjects] objectAtIndex: anyInteger]
Mubeen Qazi

Odpowiedzi:

140

Zestaw ma kilka przypadków użycia. Możesz wyliczyć przez (np. enumerateObjectsUsingBlockWith lub NSFastEnumeration), wywołać containsObjecttest członkostwa, użyć anyObjectdo pobrania elementu członkowskiego (nie losowego) lub przekonwertować go na tablicę (w dowolnej kolejności) z allObjects.

Zestaw jest odpowiedni, gdy nie chcesz duplikatów, nie dbasz o kolejność i chcesz szybkiego testowania członkostwa.

Matthew Flaschen
źródło
7
Możesz również wyszukać znany obiekt przez potencjalny duplikat, wysyłając zestawowi member:wiadomość. Jeśli zwróci nil, zestaw nie zawiera obiektu równego podanemu; jeśli zwraca wskaźnik do obiektu, to wskaźnik, który zwraca, wskazuje obiekt już w zestawie. Obiekty w zestawie muszą implementować hashi isEqual:aby było to użyteczne.
Peter Hosey,
@PeterHosey Myślę, że nie hash musi być wdrożony; gdybyś to zrobił, poszłoby znacznie szybciej.
fumoboy007
2
@ fumoboy007: Do przechowywania w zestawie lub używania jako klucza w słowniku, tak. Z dokumentacji hashprotokołu NSObject: „Jeśli dwa obiekty są równe (zgodnie z isEqual:metodą), muszą mieć tę samą wartość skrótu”. Obecnie implementacje NSObject hashi isEqual:wykorzystują tożsamość (adres) obiektu. Jeśli nadpisujesz isEqual:, ustawiasz możliwość obiektów, które nie są identyczne, ale są równe - które, jeśli nie zastąpisz również hash, będą nadal miały różne skróty. To narusza wymóg, zgodnie z którym równe obiekty mają równe skróty.
Peter Hosey,
1
@ fumoboy007: Zastanów się, jak działa tabela skrótów: kontener ma tablicę z pewną liczbą segmentów i używa skrótu każdego przychodzącego obiektu do określenia, w którym member:zasobniku powinien się znajdować ten obiekt. W przypadku wyszukiwań takich jak kontener będzie wyglądał tylko w tym jednym zasobnik (dlatego zestawy są znacznie szybsze niż tablice podczas testowania członkostwa, a słowniki są o wiele szybsze niż tablice równoległe podczas wyszukiwania klucz-wartość). Jeśli poszukiwany obiekt ma nieprawidłowy skrót, kontener będzie szukał niewłaściwego zasobnika i nie znajdzie dopasowania.
Peter Hosey,
@PeterHosey Ups… błędnie pomyślałem, że domyślna implementacja -[NSObject hash]to 0. To wiele wyjaśnia. = S
fumoboy007
31

NSSet nie ma metody objectAtIndex:

Spróbuj wywołać allObjects, który zwraca NSArray wszystkich obiektów.

Nikogo w szczególności
źródło
masz jakiś pomysł, czy zwrócona tablica jest uporządkowana? innymi słowy, jeśli dodam obiekty do zestawu za pomocą „setByAddingObject”, a użyłem „allObjects”, to czy? elementy w tablicy uporządkowane w kolejności dodania obiektów?
iosMentalist
po prostu posortuj wynikową tablicę za pomocą NSSortPredicate i wszystko będzie dobrze
Jason,
Jeśli interesuje Cię tylko jeden konkretny obiekt, lepiej użyj podejścia filterSetUsingPredicate.
akw
17

możliwe jest użycie filterSetUsingPredicate, jeśli masz jakiś unikalny identyfikator do wybrania potrzebnego obiektu.

Najpierw utwórz predykat (zakładając, że Twój unikalny identyfikator w obiekcie nazywa się „identifier” i jest to NSString):

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

A następnie wybierz obiekt za pomocą predykatu:

NSObject *myChosenObject = [mySet filteredSetUsingPredicate:myPredicate].anyObject;
Vitalii
źródło
13

NSArray *myArray = [myNSSet allObjects];

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

zamień NSUInteger na indeks żądanego obiektu.

krischu
źródło
1
Powinno być: MyObject * object = [myArray objectAtIndex: (NSUInteger *)]
John D.
8

Dla Swift3 i iOS10:

//your current set
let mySet : NSSet
//targetted index
let index : Int
//get object in set at index
let object = mySet.allObjects[index]
Kevin ABRIOUX
źródło
6

NSSet używa metody isEqual: (której obiekty, które wstawiasz do tego zestawu, muszą dodatkowo przesłonić metodę hash), aby określić, czy wewnątrz niego znajduje się obiekt.

Na przykład, jeśli masz model danych, który definiuje swoją wyjątkowość za pomocą wartości id (powiedzmy, że właściwość to:

@property NSUInteger objectID;

wtedy zaimplementowałbyś isEqual: as

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

i możesz zaimplementować 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
}

Następnie możesz pobrać obiekt z tym identyfikatorem (a także sprawdzić, czy jest w NSSet) za pomocą:

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

Ale tak, jedynym sposobem na wyciągnięcie czegoś z NSSet jest rozważenie tego, co definiuje jego wyjątkowość w pierwszej kolejności.

Nie testowałem tego, co jest szybsze, ale unikam wyliczania, ponieważ może to być liniowe, podczas gdy użycie metody member: byłoby znacznie szybsze. To jeden z powodów, dla których preferujemy używanie NSSet zamiast NSArray.

podkowa7
źródło
0
for (id currentElement in mySet)
{
    // ** some actions with currentElement 
}
Savchenko Dmitry
źródło
0

W większości przypadków nie zależy Ci na uzyskaniu jednego konkretnego obiektu z zestawu. Zależy Ci na sprawdzeniu, czy zestaw zawiera obiekt. Do tego nadają się zestawy. Jeśli chcesz sprawdzić, czy obiekt znajduje się w kolekcji, zestawy są znacznie szybsze niż tablice.

Jeśli nie zależy ci na tym, który przedmiot otrzymasz, użyj, -anyObjectktóry daje ci tylko jeden przedmiot z zestawu, na przykład włożenie dłoni do torby i złapanie czegoś.

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

Jeśli zależy ci na tym, jaki przedmiot otrzymasz, użyj opcji, -memberktóra zwraca obiekt lub nil, jeśli nie ma go w zestawie. Musisz mieć obiekt, zanim go wywołasz.

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

Oto kod, który możesz uruchomić w Xcode, aby lepiej zrozumieć

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);
}
Nevan King
źródło