Eu quero usar uma estrutura de dados de fila em meu programa Objective-C. Em C ++, eu usaria a fila STL. Qual é a estrutura de dados equivalente em Objective-C? Como faço para empurrar / abrir itens?
Eu quero usar uma estrutura de dados de fila em meu programa Objective-C. Em C ++, eu usaria a fila STL. Qual é a estrutura de dados equivalente em Objective-C? Como faço para empurrar / abrir itens?
Respostas:
A versão de Ben é uma pilha em vez de uma fila, então ajustei um pouco:
NSMutableArray + QueueAdditions.h
@interface NSMutableArray (QueueAdditions)
- (id) dequeue;
- (void) enqueue:(id)obj;
@end
NSMutableArray + QueueAdditions.m
@implementation NSMutableArray (QueueAdditions)
// Queues are first-in-first-out, so we remove objects from the head
- (id) dequeue {
// if ([self count] == 0) return nil; // to avoid raising exception (Quinn)
id headObject = [self objectAtIndex:0];
if (headObject != nil) {
[[headObject retain] autorelease]; // so it isn't dealloc'ed on remove
[self removeObjectAtIndex:0];
}
return headObject;
}
// Add to the tail of the queue (no one likes it when people cut in line!)
- (void) enqueue:(id)anObject {
[self addObject:anObject];
//this method automatically adds to the end of the array
}
@end
Basta importar o arquivo .h para onde quiser usar seus novos métodos e chamá-los como faria com qualquer outro método NSMutableArray.
Boa sorte e continue codificando!
Eu não diria que usar NSMutableArray é necessariamente a melhor solução, particularmente se você estiver adicionando métodos com categorias, devido à fragilidade que eles podem causar se os nomes dos métodos colidirem. Para uma fila rápida e suja, eu usaria os métodos para adicionar e remover no final de um array mutável. No entanto, se você planeja reutilizar a fila, ou se deseja que seu código seja mais legível e evidente, uma classe de fila dedicada é provavelmente o que você deseja.
O Cocoa não tem um integrado, mas existem outras opções, e você também não precisa escrever um do zero. Para uma fila verdadeira que apenas adiciona e remove das extremidades, uma matriz de buffer circular é uma implementação extremamente rápida. Confira CHDataStructures.framework , uma biblioteca / framework em Objective-C que venho trabalhando. Ele tem uma variedade de implementações de filas, bem como pilhas, deques, conjuntos classificados, etc. Para seus propósitos, CHCircularBufferQueue é significativamente mais rápido (ou seja, comprovável com benchmarks) e mais legível (admitidamente subjetivo) do que usar um NSMutableArray.
Uma grande vantagem de usar uma classe Objective-C nativa em vez de uma classe STL C ++ é que ela se integra perfeitamente ao código Cocoa e funciona muito melhor com codificação / decodificação (serialização). Também funciona perfeitamente com coleta de lixo e enumeração rápida (ambos presentes no 10.5+, mas apenas o último no iPhone) e você não precisa se preocupar com o que é um objeto Objective-C e o que é um objeto C ++.
Por último, embora NSMutableArray seja melhor do que um array C padrão ao adicionar e remover de qualquer extremidade, também não é a solução mais rápida para uma fila. Para a maioria dos aplicativos, é satisfatório, mas se você precisar de velocidade, um buffer circular (ou, em alguns casos, uma lista vinculada otimizada para manter as linhas de cache quentes) pode facilmente derrubar um NSMutableArray.
Até onde eu sei, Objective-C não fornece uma estrutura de dados Queue. Sua melhor aposta é criar um NSMutableArray
e, em seguida [array lastObject]
, usar [array removeLastObject]
para buscar o item e [array insertObject:o atIndex:0]
...
Se você está fazendo muito isso, pode criar uma categoria Objective-C para estender a funcionalidade da NSMutableArray
classe. As categorias permitem adicionar funções dinamicamente a classes existentes (mesmo aquelas para as quais você não tem a fonte) - você pode fazer uma fila como esta:
(NOTA: Este código é, na verdade, para uma pilha, não uma fila. Veja os comentários abaixo)
@interface NSMutableArray (QueueAdditions)
- (id)pop;
- (void)push:(id)obj;
@end
@implementation NSMutableArray (QueueAdditions)
- (id)pop
{
// nil if [self count] == 0
id lastObject = [[[self lastObject] retain] autorelease];
if (lastObject)
[self removeLastObject];
return lastObject;
}
- (void)push:(id)obj
{
[self addObject: obj];
}
@end
Não há nenhuma classe de coleção de fila real, mas NSMutableArray pode ser usado efetivamente para a mesma coisa. Você pode definir uma categoria para adicionar métodos pop / push como uma conveniência, se desejar.
Sim, use NSMutableArray. NSMutableArray é realmente implementado como árvore 2-3; você normalmente não precisa se preocupar com as características de desempenho de adicionar ou remover objetos do NSMutableArray em índices arbitrários.
As soluções que usam uma categoria em NSMutableArray
não são filas verdadeiras, porque NSMutableArray
expõe operações que são um superconjunto de filas. Por exemplo, você não deve ter permissão para remover um item do meio de uma fila (como essas soluções de categoria ainda permitem). É melhor encapsular a funcionalidade, um princípio importante do design orientado a objetos.
StdQueue.h
#import <Foundation/Foundation.h>
@interface StdQueue : NSObject
@property(nonatomic, readonly) BOOL empty;
@property(nonatomic, readonly) NSUInteger size;
@property(nonatomic, readonly) id front;
@property(nonatomic, readonly) id back;
- (void)enqueue:(id)object;
- (id)dequeue;
@end
StdQueue.m
#import "StdQueue.h"
@interface StdQueue ()
@property(nonatomic, strong) NSMutableArray* storage;
@end
@implementation StdQueue
#pragma mark NSObject
- (id)init
{
if (self = [super init]) {
_storage = [NSMutableArray array];
}
return self;
}
#pragma mark StdQueue
- (BOOL)empty
{
return self.storage.count == 0;
}
- (NSUInteger)size
{
return self.storage.count;
}
- (id)front
{
return self.storage.firstObject;
}
- (id)back
{
return self.storage.lastObject;
}
- (void)enqueue:(id)object
{
[self.storage addObject:object];
}
- (id)dequeue
{
id firstObject = nil;
if (!self.empty) {
firstObject = self.storage.firstObject;
[self.storage removeObjectAtIndex:0];
}
return firstObject;
}
@end
esta é a minha implementação, espero que ajude.
É meio minimalista, então você deve manter o controle da cabeça salvando a nova no pop e descartando a velha
@interface Queue : NSObject {
id _data;
Queue *tail;
}
-(id) initWithData:(id) data;
-(id) getData;
-(Queue*) pop;
-(void) push:(id) data;
@end
#import "Queue.h"
@implementation Queue
-(id) initWithData:(id) data {
if (self=[super init]) {
_data = data;
[_data retain];
}
return self;
}
-(id) getData {
return _data;
}
-(Queue*) pop {
return tail;
}
-(void) push:(id) data{
if (tail) {
[tail push:data];
} else {
tail = [[Queue alloc]initWithData:data];
}
}
-(void) dealloc {
if (_data) {
[_data release];
}
[super release];
}
@end
Existe algum motivo específico pelo qual você não pode simplesmente usar a fila STL? Objective C ++ é um superconjunto de C ++ (apenas use .mm como extensão em vez de .m para usar Objective C ++ em vez de Objective C). Então você pode usar o STL ou qualquer outro código C ++.
Um problema de usar a fila / vetor / lista STL etc. com objetos Objective C é que eles normalmente não suportam gerenciamento de memória de retenção / liberação / liberação automática. Isso é facilmente contornado com uma classe de contêiner C ++ Smart Pointer que retém seu objeto Objective C quando construído e o libera quando destruído. Dependendo do que você está colocando na fila STL, isso geralmente não é necessário.
-count
antecedência para verificar se há algum objeto para retirar da fila. É uma questão de preferência, na verdade.