Jeśli nie możesz pobrać obiektu z objectAtIndex: z NSSet, to w jaki sposób możesz odzyskać obiekty?
Jeśli nie możesz pobrać obiektu z objectAtIndex: z NSSet, to w jaki sposób możesz odzyskać obiekty?
Odpowiedzi:
Zestaw ma kilka przypadków użycia. Możesz wyliczyć przez (np. enumerateObjectsUsingBlock
With lub NSFastEnumeration), wywołać containsObject
test członkostwa, użyć anyObject
do 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.
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ć hash
i isEqual:
aby było to użyteczne.
hash
musi być wdrożony; gdybyś to zrobił, poszłoby znacznie szybciej.
hash
protokołu NSObject: „Jeśli dwa obiekty są równe (zgodnie z isEqual:
metodą), muszą mieć tę samą wartość skrótu”. Obecnie implementacje NSObject hash
i 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.
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.
-[NSObject hash]
to 0. To wiele wyjaśnia. = S
NSSet nie ma metody objectAtIndex:
Spróbuj wywołać allObjects, który zwraca NSArray wszystkich obiektów.
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;
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]
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.
for (id currentElement in mySet)
{
// ** some actions with currentElement
}
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, -anyObject
któ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, -member
któ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);
}
[[nsSetObjects allObjects] objectAtIndex: anyInteger]