Se você não pode obter um objeto com objectAtIndex: de um NSSet, como você recupera objetos?
Se você não pode obter um objeto com objectAtIndex: de um NSSet, como você recupera objetos?
Respostas:
Existem vários casos de uso para um conjunto. Você pode enumerar por meio de (por exemplo, com enumerateObjectsUsingBlock
ou NSFastEnumeration), chamar containsObject
para testar a associação, usar anyObject
para obter um membro (não aleatório) ou convertê-lo em uma matriz (sem nenhuma ordem específica) com allObjects
.
Um conjunto é apropriado quando você não deseja duplicatas, não se preocupa com a ordem e deseja um teste de adesão rápido.
member:
mensagem ao conjunto . Se retornar nil
, o conjunto não contém um objeto igual ao que você passou; se retornar um ponteiro de objeto, o ponteiro que ele retorna é para o objeto já no conjunto. Os objetos do conjunto devem implementar hash
e isEqual:
para que isso seja útil.
hash
precisa ser implementado; seria muito mais rápido se você fizesse isso.
hash
no protocolo NSObject: “Se dois objetos são iguais (conforme determinado pelo isEqual:
método), eles devem ter o mesmo valor de hash.” Atualmente, as implementações do NSObject hash
e isEqual:
usam a identidade do objeto (endereço). Se você substituir isEqual:
, estará configurando a possibilidade de objetos que não são idênticos, mas são iguais - os quais, se você também não substituir hash
, ainda terão hashes diferentes. Isso viola o requisito de que objetos iguais tenham hashes iguais.
member:
, o contêiner só procurará naquele bucket (é por isso que os conjuntos são muito mais rápidos do que os arrays no teste de associação e os dicionários são muito mais rápidos do que os arrays paralelos na pesquisa de valor-chave). Se o objeto procurado tiver o hash errado, o contêiner procurará no balde errado e não encontrará uma correspondência.
-[NSObject hash]
era 0. Isso explica muita coisa. = S
NSSet não tem um método objectAtIndex:
Tente chamar allObjects que retorna um NSArray de todos os objetos.
é possível usar filterSetUsingPredicate se você tiver algum tipo de identificador exclusivo para selecionar o objeto que você precisa.
Primeiro crie o predicado (supondo que seu id único no objeto é chamado de "identificador" e é um NSString):
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:@"identifier == %@", identifier];
Em seguida, escolha o objeto usando o predicado:
NSObject *myChosenObject = [mySet filteredSetUsingPredicate:myPredicate].anyObject;
Para 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]
O NSSet usa o método isEqual: (que os objetos colocados naquele conjunto devem substituir, além disso, o método hash) para determinar se um objeto está dentro dele.
Portanto, por exemplo, se você tiver um modelo de dados que define sua exclusividade por um valor de id (digamos que a propriedade seja:
@property NSUInteger objectID;
então você implementaria isEqual: as
- (BOOL)isEqual:(id)object
{
return (self.objectID == [object objectID]);
}
e você pode 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
}
Então, você pode obter um objeto com esse ID (bem como verificar se ele está no NSSet) com:
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
Mas sim, a única maneira de obter algo de um NSSet é considerando o que define sua singularidade em primeiro lugar.
Não testei o que é mais rápido, mas evito usar enumeração porque isso pode ser linear, enquanto usar o método member: seria muito mais rápido. Essa é uma das razões para preferir o uso de NSSet em vez de NSArray.
for (id currentElement in mySet)
{
// ** some actions with currentElement
}
Na maioria das vezes, você não se preocupa em obter um objeto específico de um conjunto. Você se preocupa em testar para ver se um conjunto contém um objeto. É para isso que servem os conjuntos. Quando você deseja ver se um objeto está em uma coleção, os conjuntos são muito mais rápidos do que os arrays.
Se você não se preocupam com que objeto que você começa, o uso -anyObject
que apenas lhe dá um objeto a partir do conjunto, como colocar a mão em um saco e pegar alguma coisa.
Dog *aDog = [dogs anyObject]; // dogs is an NSSet of Dog objects
Se você se preocupa com o objeto que obtém, use o -member
que devolve o objeto, ou nil se não estiver no conjunto. Você precisa já ter o objeto antes de chamá-lo.
Dog *spot = [Dog dogWithName:@"Spot"];
// ...
Dog *aDog = [dogs member:spot]; // Returns the same object as above
Aqui estão alguns códigos que você pode executar no Xcode para entender mais
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]