Ich möchte eine Warteschlangendatenstruktur in meinem Objective-C-Programm verwenden. In C ++ würde ich die STL-Warteschlange verwenden. Was ist die äquivalente Datenstruktur in Objective-C? Wie kann ich Artikel pushen / platzen lassen?
Ich möchte eine Warteschlangendatenstruktur in meinem Objective-C-Programm verwenden. In C ++ würde ich die STL-Warteschlange verwenden. Was ist die äquivalente Datenstruktur in Objective-C? Wie kann ich Artikel pushen / platzen lassen?
Antworten:
Bens Version ist ein Stapel anstelle einer Warteschlange, also habe ich sie ein wenig optimiert:
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
Importieren Sie einfach die .h-Datei, wo immer Sie Ihre neuen Methoden verwenden möchten, und rufen Sie sie wie alle anderen NSMutableArray-Methoden auf.
Viel Glück und weiter codieren!
Ich würde nicht sagen, dass die Verwendung von NSMutableArray unbedingt die beste Lösung ist, insbesondere wenn Sie Methoden mit Kategorien hinzufügen, da diese bei der Kollision von Methodennamen fragil sein können. Für eine Quick-n-Dirty-Warteschlange würde ich die Methoden verwenden, um am Ende eines veränderlichen Arrays etwas hinzuzufügen und zu entfernen. Wenn Sie jedoch vorhaben, die Warteschlange wiederzuverwenden, oder wenn Sie möchten, dass Ihr Code besser lesbar und selbstverständlich ist, ist wahrscheinlich eine dedizierte Warteschlangenklasse genau das, was Sie möchten.
In Cocoa ist keine integriert, aber es gibt andere Optionen, und Sie müssen auch keine von Grund auf neu schreiben. Für eine echte Warteschlange, die nur an den Enden hinzugefügt und entfernt wird, ist ein kreisförmiges Pufferarray eine extrem schnelle Implementierung. Schauen Sie sich CHDataStructures.framework an , eine Bibliothek / ein Framework in Objective-C, an dem ich gearbeitet habe. Es verfügt über eine Vielzahl von Implementierungen von Warteschlangen sowie von Stapeln, Deques, sortierten Mengen usw. Für Ihre Zwecke CHCircularBufferQueue ist erheblich schneller (dh mit Benchmarks nachweisbar) und lesbarer (zugegebenermaßen subjektiv) als die Verwendung eines NSMutableArray.
Ein großer Vorteil der Verwendung einer nativen Objective-C-Klasse anstelle einer C ++ STL-Klasse besteht darin, dass sie sich nahtlos in Cocoa-Code integriert und beim Codieren / Decodieren (Serialisierung) viel besser funktioniert. Es funktioniert auch perfekt mit Garbage Collection und schneller Aufzählung (beide in 10.5+ vorhanden, aber nur letztere auf dem iPhone) und Sie müssen sich keine Gedanken darüber machen, was ein Objective-C-Objekt und was ein C ++ - Objekt ist.
Obwohl NSMutableArray beim Hinzufügen und Entfernen an beiden Enden besser als ein Standard-C-Array ist, ist es auch nicht die schnellste Lösung für eine Warteschlange. Für die meisten Anwendungen ist es zufriedenstellend, aber wenn Sie Geschwindigkeit benötigen, kann ein zirkulärer Puffer (oder in einigen Fällen eine verknüpfte Liste, die optimiert ist, um die Cache-Zeilen heiß zu halten) ein NSMutableArray leicht ausschalten.
Soweit ich weiß, bietet Objective-C keine Warteschlangendatenstruktur. Ihre beste Wette ist es, einen zu erstellen NSMutableArray
und dann zu verwenden [array lastObject]
, [array removeLastObject]
um den Gegenstand zu holen, und [array insertObject:o atIndex:0]
...
Wenn Sie dies häufig tun, möchten Sie möglicherweise eine Objective-C-Kategorie erstellen, um die Funktionalität der NSMutableArray
Klasse zu erweitern. Mit Kategorien können Sie vorhandenen Klassen dynamisch Funktionen hinzufügen (auch solchen, für die Sie keine Quelle haben). Sie können eine Warteschlange wie folgt erstellen:
(HINWEIS: Dieser Code ist eigentlich für einen Stapel, nicht für eine Warteschlange. Siehe Kommentare unten)
@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
Es gibt keine echte Warteschlangensammlungsklasse, aber NSMutableArray kann effektiv für dasselbe verwendet werden. Sie können eine Kategorie definieren , um Pop / Push-Methoden hinzuzufügen, wenn Sie möchten.
Ja, verwenden Sie NSMutableArray. NSMutableArray ist tatsächlich als 2-3-Baum implementiert . In der Regel müssen Sie sich nicht mit den Leistungsmerkmalen des Hinzufügens oder Entfernens von Objekten zu NSMutableArray an beliebigen Indizes befassen.
Die Lösungen, für die eine Kategorie verwendet wird, NSMutableArray
sind keine echten Warteschlangen, daNSMutableArray
Operationen verfügbar gemacht werden, die eine Obermenge von Warteschlangen darstellen. Beispielsweise sollte es Ihnen nicht gestattet sein, ein Element aus der Mitte einer Warteschlange zu entfernen (wie dies bei diesen Kategorielösungen weiterhin möglich ist). Es ist am besten, die Funktionalität zu kapseln, ein Hauptprinzip des objektorientierten Designs.
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
Dies ist meine Implementierung, hoffe es hilft.
Ist irgendwie minimalistisch, also müssen Sie die Spur des Kopfes behalten, indem Sie den neuen Kopf beim Pop speichern und den alten Kopf wegwerfen
@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
Gibt es einen bestimmten Grund, warum Sie die STL-Warteschlange nicht einfach verwenden können? Objective C ++ ist eine Obermenge von C ++ (verwenden Sie einfach .mm als Erweiterung anstelle von .m, um Objective C ++ anstelle von Objective C zu verwenden). Dann können Sie die STL oder einen anderen C ++ - Code verwenden.
Ein Problem bei der Verwendung der STL-Warteschlange / des Vektors / der Liste usw. mit Objective C-Objekten besteht darin, dass sie normalerweise die Speicherverwaltung für Aufbewahrung / Freigabe / Autorelease nicht unterstützen. Dies lässt sich leicht mit einer C ++ Smart Pointer-Containerklasse umgehen, die ihr Objective C-Objekt beim Erstellen beibehält und es bei Zerstörung freigibt. Je nachdem, was Sie in die STL-Warteschlange stellen, ist dies häufig nicht erforderlich.
-count
vorher anrufen , um zu überprüfen, ob Objekte in die Warteschlange gestellt werden müssen. Es ist wirklich eine Frage der Präferenz.