Voglio usare una struttura di dati della coda nel mio programma Objective-C. In C ++ userei la coda STL. Qual è la struttura dati equivalente in Objective-C? Come faccio a spingere / aprire gli elementi?
Voglio usare una struttura di dati della coda nel mio programma Objective-C. In C ++ userei la coda STL. Qual è la struttura dati equivalente in Objective-C? Come faccio a spingere / aprire gli elementi?
Risposte:
La versione di Ben è una pila invece di una coda, quindi l'ho modificata un po ':
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 importare il file .h ovunque tu voglia usare i tuoi nuovi metodi e chiamarli come faresti con qualsiasi altro metodo NSMutableArray.
Buona fortuna e continua a programmare!
Non direi che l'utilizzo di NSMutableArray sia necessariamente il migliore soluzione , in particolare se stai aggiungendo metodi con categorie, a causa della fragilità che possono causare se i nomi dei metodi entrano in conflitto. Per una coda veloce e sporca, userei i metodi per aggiungere e rimuovere alla fine di un array mutabile. Tuttavia, se si prevede di riutilizzare la coda o se si desidera che il codice sia più leggibile ed evidente, una classe di coda dedicata è probabilmente ciò che si desidera.
Cocoa non ne ha uno integrato, ma ci sono altre opzioni e non devi nemmeno scriverne una da zero. Per una vera coda che aggiunge e rimuove solo dalle estremità, un array di buffer circolare è un'implementazione estremamente veloce. Dai un'occhiata a CHDataStructures.framework , una libreria / framework in Objective-C su cui ho lavorato. Ha una varietà di implementazioni di code, così come stack, deques, set ordinati, ecc. Per i tuoi scopi, CHCircularBufferQueue è significativamente più veloce (cioè dimostrabile con benchmark) e più leggibile (certamente soggettivo) rispetto all'utilizzo di NSMutableArray.
Un grande vantaggio dell'utilizzo di una classe Objective-C nativa invece di una classe STL C ++ è che si integra perfettamente con il codice Cocoa e funziona molto meglio con encode / decode (serializzazione). Funziona perfettamente anche con la garbage collection e l'enumerazione veloce (entrambe presenti in 10.5+, ma solo quest'ultima su iPhone) e non devi preoccuparti di cosa sia un oggetto Objective-C e cosa sia un oggetto C ++.
Infine, sebbene NSMutableArray sia migliore di un array C standard durante l'aggiunta e la rimozione da entrambe le estremità, non è nemmeno la soluzione più veloce per una coda. Per la maggior parte delle applicazioni è soddisfacente, ma se hai bisogno di velocità, un buffer circolare (o in alcuni casi un elenco collegato ottimizzato per mantenere calde le linee della cache) può facilmente superare un NSMutableArray.
Per quanto ne so, Objective-C non fornisce una struttura dati della coda. La soluzione migliore è creare un NSMutableArray
, quindi utilizzare [array lastObject]
, [array removeLastObject]
per recuperare l'oggetto e [array insertObject:o atIndex:0]
...
Se lo fai spesso, potresti voler creare una categoria Objective-C per estendere la funzionalità della NSMutableArray
classe. Le categorie ti consentono di aggiungere dinamicamente funzioni alle classi esistenti (anche quelle per le quali non hai l'origine): potresti creare una coda come questa:
(NOTA: questo codice è in realtà per uno stack, non una coda. Vedere i commenti di seguito)
@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
Non esiste una vera classe di raccolte di code, ma NSMutableArray può essere utilizzato in modo efficace per la stessa cosa. Puoi definire una categoria per aggiungere metodi pop / push per comodità, se lo desideri.
Sì, usa NSMutableArray. NSMutableArray è attualmente implementato come albero 2-3; in genere non è necessario preoccuparsi delle caratteristiche di prestazione dell'aggiunta o della rimozione di oggetti da NSMutableArray a indici arbitrari.
Le soluzioni che utilizzano una categoria NSMutableArray
non sono vere code, perchéNSMutableArray
espone operazioni che sono un sovrainsieme di code. Ad esempio, non dovresti essere autorizzato a rimuovere un elemento dal centro di una coda (poiché queste soluzioni di categoria ti consentono ancora di farlo). È meglio incapsulare la funzionalità, un principio fondamentale del design orientato agli oggetti.
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
questa è la mia implementazione, spero che aiuti.
È un po 'minimalista, quindi devi mantenere la traccia della testa salvando la nuova testa al pop e scartando la vecchia testa
@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
C'è qualche motivo particolare per cui non puoi usare semplicemente la coda STL? Objective C ++ è un superset di C ++ (usa semplicemente .mm come estensione invece di .m per usare Objective C ++ invece di Objective C). Quindi puoi usare STL o qualsiasi altro codice C ++.
Un problema dell'utilizzo della coda / vettore / elenco STL ecc. Con oggetti Objective C è che in genere non supportano la gestione della memoria di conservazione / rilascio / rilascio automatico. Questo può essere facilmente aggirato con una classe contenitore C ++ Smart Pointer che mantiene il suo oggetto Objective C quando viene costruito e lo rilascia quando viene distrutto. A seconda di cosa si sta inserendo nella coda AWL, spesso questo non è necessario.
-count
anticipo per verificare se ci sono oggetti da rimuovere dalla coda. È una questione di preferenza, davvero.