Как мне создать и использовать очередь в Objective-C?


107

Я хочу использовать структуру данных очереди в моей программе Objective-C. В C ++ я бы использовал очередь STL. Какая эквивалентная структура данных в Objective-C? Как мне нажимать / выталкивать предметы?

Ответы:


153

Версия Бена - это стек, а не очередь, поэтому я немного ее изменил:

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

Просто импортируйте файл .h везде, где вы хотите использовать свои новые методы, и вызывайте их, как любые другие методы NSMutableArray.

Удачи и продолжайте кодировать!


1
Я добавил закомментированную строку в начале удаления из очереди для тех, кто хочет вернуть nil, а не вызывать исключение при попытке удаления из пустой очереди. IMO, следуя поведению NSMutableArray при возникновении исключения, больше соответствует Cocoa. В конце концов, вы можете позвонить -countзаранее, чтобы проверить, есть ли какие-либо объекты для удаления из очереди. На самом деле это вопрос предпочтений.
Куинн Тейлор,

2
Я добавил этот код в репозиторий на github. Не стесняйтесь форкнуть или дайте мне знать, если я что-то не так: github.com/esromneb/ios-queue-object Спасибо !!!
portforwardpodcast

2
Мне что-то не хватает, или эта реализация имеет сложность O (n) при удалении из очереди? Это ужасно. Вам было бы намного лучше с реализацией кругового массива. эта реализация могла бы работать, но идея удаления из очереди O (n) болезненна.
ThatGuy

11
@Wolfcow, когда вы удаляете объект из индекса 0, каждый объект в массиве сдвигается на единицу вниз. Следовательно, чтобы удалить один элемент, это O (n). Вероятно, подходит для небольших очередей, что, вероятно, составляет 99% времени в мобильных приложениях, но это было бы ужасным решением для больших наборов данных в критических по времени ситуациях. Опять же, не то, чтобы вы обнаружили это в большинстве объективных ситуаций C.
ThatGuy

2
@ThatGuy Немного поздно, но NSArray реализован с кольцевым буфером, поэтому время выполнения не будет тета (N).
hhanesand

33

Я бы не сказал, что использование NSMutableArray обязательно является лучшим решением, особенно если вы добавляете методы с категориями из-за хрупкости, которую они могут вызвать, если имена методов конфликтуют. Для быстрой и грязной очереди я бы использовал методы добавления и удаления в конце изменяемого массива. Однако, если вы планируете повторно использовать очередь или хотите, чтобы ваш код был более читабельным и очевидным, вам, вероятно, нужен выделенный класс очереди.

Какао не имеет встроенного, но есть и другие варианты, и вам также не нужно писать его с нуля. Для настоящей очереди, которая только добавляет и удаляет с концов, круговой буферный массив является чрезвычайно быстрой реализацией. Ознакомьтесь с CHDataStructures.framework , библиотекой / фреймворком в Objective-C, над которой я работал. Он имеет множество реализаций очередей, а также стеки, двухсторонние очереди, отсортированные наборы и т. Д. Для ваших целей CHCircularBufferQueue значительно быстрее (т.е. доказывается с помощью тестов) и более читабелен (по общему признанию), чем использование NSMutableArray.

Одним из больших преимуществ использования собственного класса Objective-C вместо класса C ++ STL является то, что он легко интегрируется с кодом Какао и намного лучше работает с кодированием / декодированием (сериализацией). Он также отлично работает со сборкой мусора и быстрым перечислением (оба присутствуют в 10.5+, но только последнее на iPhone), и вам не нужно беспокоиться о том, что такое объект Objective-C и что такое объект C ++.

Наконец, хотя NSMutableArray лучше стандартного массива C при добавлении и удалении с любого конца, это также не самое быстрое решение для очереди. Для большинства приложений это удовлетворительно, но если вам нужна скорость, кольцевой буфер (или в некоторых случаях связанный список, оптимизированный для поддержания горячих строк кеша) может легко выйти из строя NSMutableArray.


2
Рад, что кто-то на самом деле ответил истинным решением для очереди
Casebash 01

Все ссылки не работают - где мне взять этот фреймворк? Я прочитал много хорошего об этом, но не могу найти фактический код!
amok

Фреймворк звучит многообещающе, но ссылки на SVN все еще не работают. Есть шанс где-нибудь получить код? РЕДАКТИРОВАТЬ: Получил с mac.softpedia.com/progDownload/ ... но я не вижу, текущая ли это версия
Кей

Клон репозитория Дэйва Делонга Git, кажется, является популярным репо в наши дни.
Regexident 06

29

Насколько мне известно, Objective-C не предоставляет структуру данных Queue. Лучше всего , чтобы создать NSMutableArray, а затем использовать [array lastObject], [array removeLastObject]чтобы забрать товар, и [array insertObject:o atIndex:0]...

Если вы делаете это часто, вы можете создать категорию Objective-C, чтобы расширить функциональность NSMutableArrayкласса. Категории позволяют вам динамически добавлять функции к существующим классам (даже тем, для которых у вас нет источника) - вы можете создать такую ​​очередь:

(ПРИМЕЧАНИЕ: этот код на самом деле предназначен для стека, а не для очереди. См. Комментарии ниже)

@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
Знаете ли вы, что вы реализовали здесь стек, а не очередь?
Джим Пулс,

Ах - прости! - см. модификации Wolfcow ниже.
Бен Готоу,

Согласен, если вы замените «лучший вариант» на «самый простой вариант». :-) Сторонники чистоты структуры данных и одержимые производительностью предпочли бы настоящую очередь, но NSMutableArray может легко заменить очередь.
Куинн Тейлор

3
+1 к бену, потому что мне нужно было стековое решение, даже несмотря на то, что очередь была запрошена :)
whitneyland

Все, о чем я могу думать, это о боли. Вы вставляете объект в начало массива, затем вам нужно копировать каждый элемент на 1 пробел при каждой вставке. В этом случае связанный список будет работать намного лучше.
TheM00s3

8

Реального класса коллекций очередей нет, но NSMutableArray можно эффективно использовать для того же самого. Вы можете определить категорию для добавления методов pop / push для удобства, если хотите.


Правда, NSMutableArray составляет довольно приличную очередь, хотя удаление спереди - это не то, в чем структура массива выделяется. Тем не менее, для небольших очередей производительность в любом случае не имеет большого значения. Некоторое время назад мой друг писал об этой теме в блоге ... sg80bab.blogspot.com/2008/05/…
Куинн Тейлор

7

Да, используйте NSMutableArray. NSMutableArray фактически реализован как 2-3 дерева; вам обычно не нужно беспокоиться о характеристиках производительности при добавлении или удалении объектов из NSMutableArray по произвольным индексам.


1
NSArray (и NSMutableArray по расширению) - это кластер классов, то есть он имеет несколько частных реализаций, которые могут использоваться взаимозаменяемо за кулисами. Тот, который вы получите, обычно зависит от количества элементов. Кроме того, Apple может в любое время изменить детали любой конкретной реализации. Однако вы правы, что обычно он намного более гибкий, чем стандартный массив.
Куинн Тейлор,

5

re: Wolfcow - Вот исправленная реализация метода удаления из очереди Wolfcow

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

4

Решения, использующие категорию NSMutableArray, не являются настоящими очередями, потому что NSMutableArrayпредоставляют операции, которые являются надмножеством очередей. Например, вам не должно быть разрешено удалять элемент из середины очереди (поскольку решения этих категорий по-прежнему позволяют вам это делать). Лучше всего инкапсулировать функциональность, главный принцип объектно-ориентированного дизайна.

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

кто-то может возразить, что с помощью определенных методов (например, KVC) можно получить доступ к внутреннему массиву хранения и управлять им напрямую, но это намного лучше, чем использование категории.
vikingosegundo 08

3

это моя реализация, надеюсь, поможет.

Является своего рода минималистичным, поэтому вы должны отслеживать голову, сохраняя новую голову при появлении и отбрасывая старую.

@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

Есть ли какая-то конкретная причина, по которой вы не можете просто использовать очередь STL? Objective C ++ - это надмножество C ++ (просто используйте .mm в качестве расширения вместо .m, чтобы использовать Objective C ++ вместо Objective C). Затем вы можете использовать STL или любой другой код C ++.

Одна из проблем использования очереди / вектора / списка STL с объектами Objective C заключается в том, что они обычно не поддерживают управление сохранением / освобождением / автоматическим освобождением памяти. Это легко обойти с помощью класса контейнера C ++ Smart Pointer, который сохраняет свой объект Objective C при создании и освобождает его при уничтожении. В зависимости от того, что вы помещаете в очередь STL, в этом часто нет необходимости.


1
На самом деле это не кажется хорошей идеей ... просто потому, что вы можете что-то сделать, не означает, что вы должны. Использование всей экосистемы STL и C ++ только для класса очереди определенно излишне.
extropic-engine

3
Собственно, с тех пор, как это было опубликовано, эта идея стала намного лучше. Objective C ++ / ARC означает, что вы можете использовать контейнеры STL с указателями объектов Objective C, и все это просто работает. ARC автоматически позаботится об управлении памятью в структурах C ++. Я также обычно утверждаю, что C ++, будучи намного лучшим C, делает Objective-C ++ лучшим выбором в целом, чем простой Objective C (например, с такими вещами, как класс enum). И я очень сомневаюсь, что добавление STL / C ++ заметно повлияет на размер любого реального приложения.
Peter N Lewis

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.