Come faccio a creare e utilizzare una coda in Objective-C?


107

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:


153

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!


1
Ho aggiunto una riga commentata all'inizio della rimozione dalla coda per coloro che desiderano restituire zero anziché sollevare un'eccezione quando si tenta di rimuovere la coda da una coda vuota. IMO, seguendo il comportamento NSMutableArray di sollevare un'eccezione è più coerente con Cocoa. Dopo tutto, puoi chiamare in -countanticipo per verificare se ci sono oggetti da rimuovere dalla coda. È una questione di preferenza, davvero.
Quinn Taylor

2
Ho aggiunto questo codice a un repository GitHub. Sentiti libero di fare un fork o fammi sapere se ho sbagliato qualcosa: github.com/esromneb/ios-queue-object Grazie !!!
portforwardpodcast

2
Mi manca qualcosa o questa implementazione ha una complessità O (n) in fase di rimozione dalla coda? È terribile. Staresti molto meglio con un'implementazione di array circolare. questa implementazione potrebbe funzionare, ma l'idea di O (n) dequeue è dolorosa.
ThatGuy

11
@ Wolfcow, quando rimuovi un oggetto dall'indice 0, ogni oggetto nell'array viene spostato verso il basso di uno. Pertanto, per rimuovere un singolo elemento, è O (n). Probabilmente va bene per piccole code, che è probabilmente il 99% del tempo nelle applicazioni mobili, ma questa sarebbe una soluzione terribile per grandi set di dati in situazioni critiche. Ancora una volta, non che lo troverai nella maggior parte delle situazioni oggettive C.
ThatGuy

2
@ThatGuy Un po 'in ritardo, ma NSArray è implementato con un buffer circolare, quindi il runtime non sarà theta (N).
hhanesand

33

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.


2
Sono contento che qualcuno abbia effettivamente risposto con una vera soluzione di coda
Casebash

Tutti i collegamenti sono interrotti: dove trovo quel framework? Ho letto molte cose interessanti a riguardo ma non riesco a trovare il codice effettivo!
Amok

Il framework sembra promettente ma i collegamenti a SVN sono ancora interrotti. Qualche possibilità di ottenere il codice da qualche parte? EDIT: l'ho preso da mac.softpedia.com/progDownload/… ma non riesco a vedere se questa è la versione corrente
Kay

Il clone del repository Git di Dave DeLong sembra essere il repository preferito in questi giorni.
Regexident

29

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 NSMutableArrayclasse. 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

7
Sei consapevole di aver implementato uno stack qui, non una coda?
Jim Puls,

Ahh - scusa! - vedere le modifiche di Wolfcow di seguito.
Ben Gotow

Sono d'accordo se sostituisci "la migliore scommessa" con "l'opzione più semplice". :-) I puristi della struttura dei dati e gli ossessori delle prestazioni preferirebbero una vera coda, ma un NSMutableArray può facilmente sostituire una coda.
Quinn Taylor,

3
+1 a ben perché volevo una soluzione di stack anche se è stata richiesta una coda :)
whitneyland

Tutto quello a cui riesco a pensare è il dolore. Stai inserendo un oggetto all'inizio di un array, devi quindi copiare ogni elemento su 1 spazio ogni volta che lo inserisci. In questo caso una lista collegata funzionerà molto meglio.
TheM00s3

8

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.


È vero, un NSMutableArray fa una coda abbastanza decente, sebbene la rimozione dalla parte anteriore non sia qualcosa in cui una struttura di array eccelle. Anche così, per piccole code, le prestazioni non sono comunque una preoccupazione importante. Un mio amico ha scritto
Quinn Taylor

7

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.


1
NSArray (e NSMutableArray per estensione) è un cluster di classi, il che significa che ha diverse implementazioni private che possono essere utilizzate in modo intercambiabile dietro le quinte. Quello che ottieni di solito dipende dal numero di elementi. Inoltre, Apple è libera di modificare i dettagli di qualsiasi implementazione in qualsiasi momento. Tuttavia, hai ragione sul fatto che di solito è molto più flessibile di un array standard.
Quinn Taylor,

5

re: Wolfcow - Ecco un'implementazione corretta del metodo di rimozione dalla coda di Wolfcow

- (id)dequeue {
    if ([self count] == 0) {
        return nil;
    }
    id queueObject = [[[self objectAtIndex:0] retain] autorelease];
    [self removeObjectAtIndex:0];
    return queueObject;
}

4

Le soluzioni che utilizzano una categoria NSMutableArraynon 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

si potrebbe sostenere che con alcune tecniche (ad esempio KVC) è possibile accedere e manipolare direttamente l'array di archiviazione interno, ma molto meglio rispetto all'utilizzo di una categoria.
vikingosegundo

3

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

2

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.


1
Questa non sembra proprio una buona idea ... solo perché puoi fare qualcosa non significa che dovresti. Tirare l'intero ecosistema STL e C ++ solo per una classe di coda è decisamente eccessivo.
motore estropico

3
In realtà, da quando è stato pubblicato, questa è diventata un'idea molto migliore. Objective C ++ / ARC significa che puoi usare contenitori STL con puntatori a oggetti Objective C e tutto funziona. ARC si occupa automaticamente della gestione della memoria all'interno delle strutture C ++. Direi anche che in generale C ++, essendo un C molto migliore, rende Objective-C ++ una scelta migliore in generale rispetto al semplice Objective C (dando ad esempio cose come enum class). E dubito fortemente che l'aggiunta di STL / C ++ abbia un impatto notevole sulle dimensioni di qualsiasi app del mondo reale.
Peter N Lewis

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.