Obtendo um objeto de um NSSet

93

Se você não pode obter um objeto com objectAtIndex: de um NSSet, como você recupera objetos?

ninja ninja
fonte
1
[[nsSetObjects allObjects] objectAtIndex: anyInteger]
Mubeen Qazi

Respostas:

139

Existem vários casos de uso para um conjunto. Você pode enumerar por meio de (por exemplo, com enumerateObjectsUsingBlockou NSFastEnumeration), chamar containsObjectpara testar a associação, usar anyObjectpara 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.

Matthew Flaschen
fonte
7
Você também pode pesquisar um objeto conhecido por uma possível duplicata, enviando uma 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 hashe isEqual:para que isso seja útil.
Peter Hosey
@PeterHosey Acho que não hash precisa ser implementado; seria muito mais rápido se você fizesse isso.
fumoboy007
2
@ fumoboy007: Para armazenar em um conjunto ou usar como uma chave em um dicionário, sim. Da documentação de hashno 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 hashe 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.
Peter Hosey
1
@ fumoboy007: Considere como uma tabela de hash funciona: o contêiner tem uma matriz de um certo número de baldes e usa o hash de cada objeto de entrada para determinar em qual depósito esse objeto deve estar. Para pesquisas como 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.
Peter Hosey
@PeterHosey Oops… Eu pensei erroneamente que a implementação padrão de -[NSObject hash]era 0. Isso explica muita coisa. = S
fumoboy007
31

NSSet não tem um método objectAtIndex:

Tente chamar allObjects que retorna um NSArray de todos os objetos.

Ninguém em particular
fonte
você tem alguma ideia se o array retornado está ordenado? em outras palavras, se estou adicionando objetos ao conjunto usando "setByAddingObject" e usei "allObjects", os elementos do array ordenados na ordem em que adicionei os objetos?
iosMentalist
basta classificar a matriz resultante com um NSSortPredicate e você ficará bem
Jason,
Se você estiver interessado apenas em um objeto específico, é melhor usar a abordagem filterSetUsingPredicate.
akw
17

é 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;
Vitalii
fonte
13

NSArray *myArray = [myNSSet allObjects];

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

substitua NSUInteger pelo índice do objeto desejado.

Krischu
fonte
1
Deve ser: MyObject * object = [myArray objectAtIndex: (NSUInteger *)]
John D.
8

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]
Kevin ABRIOUX
fonte
6

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.

ferradura 7
fonte
0
for (id currentElement in mySet)
{
    // ** some actions with currentElement 
}
Savchenko Dmitry
fonte
0

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 -anyObjectque 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 -memberque 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);
}
Nevan King
fonte