Qual é a maneira usual de armazenar estruturas c em um NSArray
? Vantagens, desvantagens, manipulação de memória?
Notavelmente, qual é a diferença entre valueWithBytes
e valueWithPointer
- criado por justin e bagre abaixo.
Aqui está um link para a discussão da Apple sobre valueWithBytes:objCType:
para futuros leitores ...
Para pensar lateralmente e olhar mais para o desempenho, Evgen levantou a questão do uso STL::vector
em C ++ .
(Isso levanta uma questão interessante: existe uma biblioteca c rápida, não diferente, STL::vector
mas muito mais leve, que permite o mínimo "manuseio organizado 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 sua própria estrutura como essa em um NSArray
, e como você lida com a memória nesse idioma?
Observe que estou procurando especificamente o idioma usual para armazenar structs. Claro, pode-se evitar o problema fazendo uma pequena classe nova. No entanto, quero saber como funciona o idioma usual para realmente colocar structs em um array, obrigado.
BTW, aqui está a abordagem NSData que talvez seja? não é 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, veja como armazenar CGPoints
e semelhantes em NSArray
:
NSArray *points = [NSArray arrayWithObjects:
[NSValue valueWithCGPoint:CGPointMake(6.9, 6.9)],
[NSValue valueWithCGPoint:CGPointMake(6.9, 6.9)],
nil];
(veja Como posso adicionar objetos CGPoint a um NSArray da maneira mais fácil? )
Respostas:
NSValue não suporta apenas estruturas CoreGraphics - você também pode usá-lo para si. Eu recomendaria fazer isso, pois a classe provavelmente é mais leve do que
NSData
para estruturas de dados simples.Basta usar uma expressão como a seguinte:
E para obter o valor de volta:
fonte
p
, não um ponteiro para ela. A@encode
diretiva fornece todas as informações necessárias sobre o tamanho da estrutura. Quando você libera oNSValue
(ou quando o array o faz), sua cópia da estrutura é destruída. Se você já usougetValue:
, você está bem. Consulte a seção "Usando valores" de "Tópicos de programação de números e valores": developer.apple.com/library/ios/documentation/Cocoa/Conceptual/…@encode
descreveria a estrutura com esse ponteiro, mas não descreveria totalmente os dados apontados, que poderiam de fato mudar.NSValue
automaticamente a memória da estrutura quando ela for desalocada? A documentação é um pouco confusa sobre isso.NSValue
possui os dados que copia para si mesmo e não tenho que me preocupar em liberá-lo (no ARC)?NSValue
não faz nenhum “gerenciamento de memória” per se - você pode pensar nisso como apenas ter uma cópia do valor da estrutura internamente. Se a estrutura contivesse ponteiros aninhados, por exemplo,NSValue
não saberia como liberar, copiar ou fazer qualquer coisa com eles - isso os deixaria intactos, copiando o endereço como está.Eu sugeriria que você seguisse o
NSValue
caminho, mas se você realmente deseja armazenarstruct
tipos de dados simples em seu NSArray (e outros objetos de coleção em Cocoa), você pode fazer isso - embora indiretamente, usando Core Foundation e ligação gratuita .CFArrayRef
(e sua contraparte mutávelCFMutableArrayRef
) fornecem ao desenvolvedor mais flexibilidade ao criar um objeto de array. Veja o quarto argumento do inicializador designado:Isso permite que você solicite que o
CFArrayRef
objeto use as rotinas de gerenciamento de memória do Core Foundation, nenhuma ou mesmo suas próprias rotinas de gerenciamento de memória.Exemplo obrigatório:
O exemplo acima ignora completamente os problemas com relação ao gerenciamento de memória. A estrutura
myStruct
é criada na pilha e, portanto, é destruída quando a função termina - o array conterá um ponteiro para um objeto que não está mais lá. Você pode contornar isso usando suas próprias rotinas de gerenciamento de memória - daí porque a opção é fornecida a você - mas então você tem que fazer o trabalho duro de contagem de referência, alocação de memória, desalocação e assim por diante.Eu não recomendaria esta solução, mas a manterei aqui para o caso de ser do interesse de outra pessoa. :-)
O uso de sua estrutura conforme alocado no heap (no lugar da pilha) é demonstrado aqui:
fonte
struct
, certamente poderia alocá-lo uma vez e não liberá-lo no futuro. Incluí um exemplo disso em minha resposta editada. Além disso, não era um erro de digitaçãomyStruct
, já que era uma estrutura alocada na pilha, diferente de um ponteiro para uma estrutura alocada na pilha.Um método semelhante para adicionar c struct é armazenar o ponteiro e des-referenciar o ponteiro como tal;
fonte
se você está se sentindo nerd ou realmente tem muitas classes para criar: ocasionalmente é útil construir dinamicamente uma classe objc (ref:)
class_addIvar
. dessa forma, você pode criar classes objc arbitrárias de tipos arbitrários. você pode especificar campo por campo, ou apenas passar as informações da estrutura (mas isso está praticamente replicando NSData). às vezes útil, mas provavelmente mais um 'fato engraçado' para a maioria dos leitores.você pode chamar class_addIvar e adicionar uma variável de instância Megapoint a uma nova classe, ou você pode sintetizar uma variante objc da classe Megapoint em tempo de execução (por exemplo, uma variável de instância para cada campo do Megapoint).
o primeiro é equivalente à classe objc compilada:
o último é equivalente à classe objc compilada:
depois de adicionar os ivars, você pode adicionar / sintetizar métodos.
para ler os valores armazenados na extremidade receptora, use seus métodos sintetizados
object_getInstanceVariable
, ouvalueForKey:
(que frequentemente converterão essas variáveis 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 relativas à memória, velocidade, facilidade de manutenção, facilidade de transferência ou arquivamento, etc. determinarão qual é o melhor para um determinado caso ... mas não existe uma solução "perfeita" que seja ideal em todos os aspectos. não há '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 específico, caso ou conjunto de requisitos ' - o que você teria especificar.
além disso, NSArray é uma interface de array geralmente reutilizável para tipos de ponteiros (ou menores), mas há outros contêineres que são mais adequados para c-structs por muitas razões (std :: vector sendo uma escolha típica para c-structs).
fonte
std::vector
(por exemplo) é mais adequado para manter tipos, estruturas e classes C / C ++ do que NSArray. Ao usar um NSArray dos tipos NSValue, NSData ou NSDictionary, você está perdendo muita segurança de tipo ao adicionar uma tonelada de alocações e sobrecarga de tempo de execução. Se você quiser ficar com C, eles geralmente usam malloc e / ou matrizes na pilha ... masstd::vector
esconde a maioria das complicações de você.seria melhor usar o serializador objc do pobre se você estiver compartilhando esses dados em vários abis / arquiteturas:
... particularmente se a estrutura pode mudar ao longo do tempo (ou por plataforma de destino). não é tão rápido quanto outras opções, mas é menos provável que falhe em algumas condições (que você não especificou como importantes ou não).
se a representação serializada não sair do processo, então o tamanho / ordem / alinhamento de estruturas arbitrárias não deve mudar, e há opções que são mais simples e rápidas.
em ambos os casos, você já está adicionando um objeto ref-counted (em comparação com NSData, NSValue), então ... criar uma classe objc que contenha o Megapoint é a resposta certa em muitos casos.
fonte
Eu sugiro que você use std :: vector ou std :: list para tipos C / C ++, porque no início é apenas mais rápido do que NSArray e, no segundo, 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 mecanismos modernos de jogos, física e áudio para dispositivos móveis usam contêineres STL para armazenar dados internos. Só porque eles são muito rápidos.
Se não for para você - há boas respostas dos caras sobre NSValue - acho que é mais aceitável.
fonte
Megapoint pt[8];
- esta é uma opção em c ++ e contêineres C ++ especializados (por exemplo,std::array
) - observe também que o exemplo não adiciona o alinhamento especial (16 bytes foram escolhidos porque é do tamanho de um Megapoint). (cont.)std::vector
irá adicionar uma pequena quantidade de overhead a isso, e uma alocação (se você souber o tamanho de que precisará) ... mas isso está mais próximo do metal do que mais de 99,9% dos gabinetes precisam. normalmente, você apenas usaria um vetor, a menos que o tamanho seja fixo ou tenha um máximo razoável.Em vez de tentar colocar c struct em um NSArray, você pode colocá-los em um NSData ou NSMutableData como um array ac de structs. Para acessá-los, você deve fazer
ou para definir então
fonte
Embora o uso de um NSValue funcione bem para armazenar estruturas como um objeto Obj-C, você não pode codificar um NSValue contendo uma estrutura com NSArchiver / NSKeyedArchiver. Em vez disso, você deve codificar membros de estrutura individuais ...
Consulte o Guia de programação de arquivos e serializações da Apple> Estruturas e campos de bits
fonte
Para sua estrutura, você pode adicionar um atributo
objc_boxable
e usar a@()
sintaxe para colocar sua estrutura na instância NSValue sem chamarvalueWithBytes:objCType:
:fonte
Um objeto Obj C é apenas uma estrutura C com alguns elementos adicionados. Portanto, basta criar uma classe personalizada e você terá o tipo de estrutura C que um NSArray requer. Qualquer estrutura C que não tenha o fragmento extra que um NSObject inclui em sua estrutura C será indigesta para um NSArray.
Usar NSData como um wrapper pode apenas armazenar uma cópia dos structs e não dos structs originais, se isso fizer alguma diferença para você.
fonte
Você pode usar classes NSObject diferentes das C-Structures para armazenar informações. E você pode armazenar facilmente esse NSObject no NSArray.
fonte