Qual é a melhor maneira de colocar uma estrutura c em um NSArray?

Qual é a maneira usual de armazenar estruturas c em um NSArray ? Vantagens, desvantagens, manuseio de memory?

Notavelmente, qual é a diferença entre valueWithBytes e valueWithPointer – levantado por justin e bagre abaixo.

Aqui está um link para a discussão da Apple sobre valueWithBytes:objCType: para futuros leitores …

Para um pouco de pensamento lateral e um melhor desempenho, Evgen levantou a questão de usar o STL::vector em C ++ .

(Isso levanta uma questão interessante: existe uma biblioteca c rápida, não diferente de STL::vector mas muito mais leve, que permite o mínimo “manuseio arrumado de matrizes” …?)

Então a pergunta original …

Por exemplo:

 typedef struct _Megapoint { float w,x,y,z; } Megapoint; 

Então: qual é a maneira normal, melhor e idiomática de armazenar a própria estrutura como em um NSArray , e como você lida com a memory nesse idioma?

Por favor, note que estou procurando especificamente o idioma usual para armazenar estruturas. Evidentemente, pode-se evitar o problema fazendo uma nova pequena turma. No entanto, eu quero saber como o idioma usual para realmente colocar estruturas em uma matriz, obrigado.

BTW aqui está a abordagem NSData que é talvez? não é melhor …

 Megapoint p; NSArray *a = [NSArray arrayWithObjects: [NSData dataWithBytes:&p length:sizeof(Megapoint)], [NSData dataWithBytes:&p length:sizeof(Megapoint)], [NSData dataWithBytes:&p length:sizeof(Megapoint)], nil]; 

BTW como um ponto de referência e graças a Jarret Hardie, aqui está como armazenar CGPoints e similares em um NSArray :

 NSArray *points = [NSArray arrayWithObjects: [NSValue valueWithCGPoint:CGPointMake(6.9, 6.9)], [NSValue valueWithCGPoint:CGPointMake(6.9, 6.9)], nil]; 

(consulte Como posso adicionar objects CGPoint a um NSArray da maneira mais fácil? )

O NSValue não suporta apenas estruturas do CoreGraphics – você também pode usá-lo para o seu próprio. Eu recomendaria fazer isso, já que a class provavelmente é mais leve que NSData para estruturas de dados simples.

Basta usar uma expressão como a seguinte:

 [NSValue valueWithBytes:&p objCType:@encode(Megapoint)]; 

E para recuperar o valor:

 Megapoint p; [value getValue:&p]; 

Eu sugiro que você fique com a rota NSValue , mas se você realmente deseja armazenar tipos de dados simples em seu NSArray (e outros objects de coleção em Cocoa), você pode fazê-lo – embora indiretamente, usando Core Foundation e toll- ponte livre .

CFArrayRef (e seu equivalente mutável, CFMutableArrayRef ) proporciona ao desenvolvedor mais flexibilidade ao criar um object de matriz. Veja o quarto argumento do inicializador designado:

 CFArrayRef CFArrayCreate ( CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFArrayCallBacks *callBacks ); 

Isso permite que você solicite que o object CFArrayRef use as rotinas de gerenciamento de memory do Core Foundation, nenhuma ou até mesmo suas próprias rotinas de gerenciamento de memory.

Exemplo obrigatório:

 // One would pass &kCFTypeArrayCallBacks (in lieu of NULL) if using CF types. CFMutableArrayRef arrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL); NSMutableArray *array = (NSMutableArray *)arrayRef; struct {int member;} myStruct = {.member = 42}; // Casting to "id" to avoid compiler warning [array addObject:(id)&myStruct]; // Hurray! struct {int member;} *mySameStruct = [array objectAtIndex:0]; 

O exemplo acima ignora completamente os problemas relacionados ao gerenciamento de memory. A estrutura myStruct é criada na pilha e, portanto, é destruída quando a function termina – a matriz conterá um ponteiro para um object que não está mais lá. Você pode contornar isso usando suas próprias rotinas de gerenciamento de memory – daí porque a opção é fornecida a você – mas então você tem que fazer o árduo trabalho de contagem de referência, alocar memory, desalocá-la e assim por diante.

Eu não recomendaria essa solução, mas a manterá aqui, caso seja de interesse de qualquer outra pessoa. 🙂


Usar sua estrutura como alocada no heap (em vez da pilha) é demonstrado aqui:

 typedef struct { float w, x, y, z; } Megapoint; // One would pass &kCFTypeArrayCallBacks (in lieu of NULL) if using CF types. CFMutableArrayRef arrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL); NSMutableArray *array = (NSMutableArray *)arrayRef; Megapoint *myPoint = malloc(sizeof(Megapoint); myPoint->w = 42.0f; // set ivars as desired.. // Casting to "id" to avoid compiler warning [array addObject:(id)myPoint]; // Hurray! Megapoint *mySamePoint = [array objectAtIndex:0]; 

se você está se sentindo nerd, ou realmente tem um monte de classs para criar: ocasionalmente é útil construir dinamicamente uma class objc (ref: class_addIvar ). Desta forma, você pode criar classs objc arbitrárias a partir de tipos arbitrários. você pode especificar campo por campo, ou apenas passar a informação da estrutura (mas isso praticamente está replicando o NSData). às vezes útil, mas provavelmente mais de um “fato divertido” para a maioria dos leitores.

Como eu aplicaria isso aqui?

você pode chamar class_addIvar e adicionar uma variável de instância Megapoint a uma nova class, ou você pode sintetizar uma variante objc da class Megapoint em tempo de execução (por exemplo, uma variável de instância para cada campo do Megapoint).

o primeiro é equivalente à class objc compilada:

 @interface MONMegapoint { Megapoint megapoint; } @end 

o último é equivalente à class objc compilada:

 @interface MONMegapoint { float w,x,y,z; } @end 

depois de adicionar os ivars, você pode adicionar / sintetizar methods.

para ler os valores armazenados na extremidade de recebimento, use seus methods sintetizados, object_getInstanceVariable ou valueForKey: (que frequentemente converterá essas variables ​​de instância escalar em representações NSNumber ou NSValue).

btw: todas as respostas que você recebeu são úteis, algumas são melhores / piores / inválidas dependendo do contexto / cenário. necessidades específicas de memory, velocidade, facilidade de manutenção, facilidade de transferência ou arquivamento, etc. determinarão o que é melhor para um determinado caso … mas não existe uma solução “perfeita” que seja ideal em todos os aspectos. não existe uma ‘melhor maneira de colocar uma c-struct em um NSArray’, apenas uma ‘melhor maneira de colocar uma c-struct em um NSArray para um cenário, caso ou conjunto de requisitosespecífico – o que você teria especificar.

além disso, o NSArray é uma interface de matriz geralmente reutilizável para tipos de tamanho de ponteiro (ou menores), mas há outros recipientes que são mais adequados para estruturas c por várias razões (std :: vector sendo uma opção típica para estruturas c).

seria melhor usar o serializador objc do pobre se você estiver compartilhando esses dados em várias abis / arquiteturas:

 Megapoint mpt = /* ... */; NSMutableDictionary * d = [NSMutableDictionary new]; assert(d); /* optional, for your runtime/deserialization sanity-checks */ [d setValue:@"Megapoint" forKey:@"Type-Identifier"]; [d setValue:[NSNumber numberWithFloat:mpt.w] forKey:@"w"]; [d setValue:[NSNumber numberWithFloat:mpt.x] forKey:@"x"]; [d setValue:[NSNumber numberWithFloat:mpt.y] forKey:@"y"]; [d setValue:[NSNumber numberWithFloat:mpt.z] forKey:@"z"]; NSArray *a = [NSArray arrayWithObject:d]; [d release], d = 0; /* ... */ 

… particularmente se a estrutura puder mudar ao longo do tempo (ou por plataforma direcionada). não é tão rápido quanto as outras opções, mas é menos provável que ele quebre algumas condições (que você não especificou como importantes ou não).

se a representação serializada não sair do processo, o tamanho / ordem / alinhamento das estruturas arbitrárias não deve mudar, e há opções mais simples e rápidas.

em ambos os casos, você já está adicionando um object ref-counted (comparado com NSData, NSValue) então … criar uma class objc que mantenha o Megapoint é a resposta correta em muitos casos.

Um método semelhante para adicionar c struct é armazenar o ponteiro e desreferenciar o ponteiro como assim;

 typedef struct BSTNode { int data; struct BSTNode *leftNode; struct BSTNode *rightNode; }BSTNode; BSTNode *rootNode; //declaring a NSMutableArray @property(nonatomic)NSMutableArray *queues; //storing the pointer in the array [self.queues addObject:[NSValue value:&rootNode withObjCType:@encode(BSTNode*)]]; //getting the value BSTNode *frontNode =[[self.queues objectAtIndex:0] pointerValue]; 

Eu sugiro que você use std :: vector ou std :: list para os tipos C / C ++, porque no começo é apenas mais rápido que o NSArray, e na segunda se não houver velocidade suficiente para você – você sempre pode criar o seu próprio alocadores para contêineres STL e torná-los ainda mais rápidos. Todos os modernos mecanismos móveis de jogos, física e áudio usam contêineres STL para armazenar dados internos. Só porque eles realmente são rápidos.

Se não é para você – há boas respostas de caras sobre NSValue – eu acho que é mais aceitável.

Em vez de tentar colocar c struct em um NSArray, você pode colocá-los em um NSData ou NSMutableData como uma matriz de structs. Para acessá-los você faria o

 const struct MyStruct * theStruct = (const struct MyStruct*)[myData bytes]; int value = theStruct[2].integerNumber; 

ou definir então

 struct MyStruct * theStruct = (struct MyStruct*)[myData mutableBytes]; theStruct[2].integerNumber = 10; 

Ao usar um NSValue funciona bem para armazenar estruturas como um object Obj-C, você não pode codificar um NSValue contendo uma estrutura com NSArchiver / NSKeyedArchiver. Em vez disso, você tem que codificar membros de struct individuais …

Veja o Guia de Programação de Arquivos e Serialização da Apple> Estruturas e Bit Fields

Um object Obj C é apenas uma estrutura C com alguns elementos adicionados. Então, basta criar uma class personalizada e você terá o tipo de estrutura C que um NSArray requer. Qualquer C struct que não tenha o acréscimo extra que um NSObject inclui dentro de sua estrutura C será indigesto para um NSArray.

Usar o NSData como um wrapper pode estar armazenando apenas uma cópia das estruturas e não as estruturas originais, se isso fizer diferença para você.

Você pode usar classs NSObject diferentes das estruturas C para armazenar informações. E você pode facilmente armazenar esse NSObject no NSArray.