【问题标题】:How do I make and use a Queue in Objective-C?如何在 Objective-C 中创建和使用队列?
【发布时间】:2010-10-23 11:31:40
【问题描述】:

我想在我的 Objective-C 程序中使用队列数据结构。在 C++ 中,我会使用 STL 队列。 Objective-C 中的等效数据结构是什么?如何推送/弹出项目?

【问题讨论】:

    标签: objective-c cocoa data-structures queue


    【解决方案1】:

    Ben 的版本是堆栈而不是队列,所以我稍微调整了一下:

    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 方法一样调用它们。

    祝你好运,继续编码!

    【讨论】:

    • 我在 dequeue 的开头为那些希望返回 nil 而不是在尝试从空队列中出列时引发异常的人添加了一个注释掉的行。 IMO,遵循引发异常的 NSMutableArray 行为与 Cocoa 更一致。毕竟,您可以事先调用-count 来检查是否有任何对象要出列。这是一个偏好问题,真的。
    • 我已将此代码添加到 github 存储库中。如果我有什么问题,请随时分叉或让我知道:github.com/esromneb/ios-queue-object 谢谢!!!
    • 我是否遗漏了什么,或者这个实现在出队时是否具有 O(n) 复杂性?这太可怕了。使用循环数组实现会更好。这种实现可能可行,但 O(n) 出队的想法很痛苦。
    • @Wolfcow,当您从索引 0 中删除一个对象时,数组中的每个对象都会向下移动一个。因此,要删除单个项目,它是 O(n)。对于小型队列来说可能没问题,这可能是移动应用程序中 99% 的时间,但对于时间紧迫的情况下的大型数据集,这将是一个糟糕的解决方案。同样,在大多数客观 C 情况下,您不会发现这一点。
    • @ThatGuy 有点晚了,但是 NSArray 是用循环缓冲区实现的,所以运行时不会是 theta(N)。
    【解决方案2】:

    我不会说使用 NSMutableArray 一定是最佳解决方案,特别是如果您要添加带有类别的方法,因为如果方法名称发生冲突,它们可能会导致脆弱性。对于 quick-n-dirty 队列,我将使用在可变数组末尾添加和删除的方法。但是,如果您打算重用队列,或者希望您的代码更具可读性和不言而喻,那么您可能想要一个专用的队列类。

    Cocoa 没有内置的,但还有其他选项,您也不必从头开始编写。对于仅从末端添加和删除的真正队列,循环缓冲区数组是一种非常快速的实现。查看CHDataStructures.framework,这是我一直在研究的Objective-C 库/框架。它有多种队列实现,以及堆栈、双端队列、排序集等。为了您的目的,CHCircularBufferQueue 比使用 NSMutableArray 更快(即可以通过基准测试证明)和更具可读性(诚然是主观的)。

    使用原生 Objective-C 类而不是 C++ STL 类的一大优势是它与 Cocoa 代码无缝集成,并且在编码/解码(序列化)方面工作得更好。它还可以完美地与垃圾收集和快速枚举配合使用(两者都存在于 10.5+ 中,但只有后者在 iPhone 上),您不必担心什么是 Objective-C 对象以及什么是 C++ 对象。

    最后,虽然 NSMutableArray 在从任一端添加和删除时都优于标准 C 数组,但它也不是队列的最快解决方案。对于大多数应用程序来说它是令人满意的,但如果您需要速度,循环缓冲区(或在某些情况下为保持高速缓存行热而优化的链表)可以轻松击败 NSMutableArray。

    【讨论】:

    • 很高兴有人回复了真正的队列解决方案
    • 所有链接都已损坏 - 我从哪里获得该框架?我已经阅读了很多关于它的好东西,但找不到实际的代码!
    • 该框架听起来很有希望,但与 SVN 的链接仍然断开。有机会在某处获得代码吗?编辑:从mac.softpedia.com/progDownload/… 得到它,但我看不出这是否是当前版本
    • Dave DeLong 的 Git repo clone 似乎是这些天的首选仓库。
    【解决方案3】:

    据我所知,Objective-C 不提供 Queue 数据结构。最好的办法是创建一个NSMutableArray,然后使用[array lastObject][array removeLastObject] 获取项目,然后使用[array insertObject:o atIndex:0]...

    如果您经常这样做,您可能希望创建一个 Objective-C 类别来扩展 NSMutableArray 类的功能。类别允许您将函数动态添加到现有类(即使是您没有源代码的类) - 您可以像这样创建一个队列:

    (注意:此代码实际上是针对堆栈,而不是队列。请参阅下面的 cmets)

    @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
    

    【讨论】:

    • 你知道你在这里实现了一个堆栈,而不是一个队列吗?
    • 啊——对不起! - 请参阅下面 Wolfcow 的修改。
    • 如果您将“最佳选择”替换为“最简单的选项”,我会同意。 :-) 数据结构纯粹主义者和性能痴迷者更喜欢真正的队列,但 NSMutableArray 可以轻松地代替队列。
    • +1 to be 因为我想要一个堆栈解决方案,即使要求队列:)
    • 我能想到的只有痛苦。您在数组的开头插入一个对象,然后每次插入时都需要将每个元素复制到 1 个空间。在这种情况下,链表会表现得更好。
    【解决方案4】:

    没有真正的队列集合类,但是 NSMutableArray 可以有效地用于同样的事情。如果需要,您可以定义 category 来添加 pop/push 方法以方便使用。

    【讨论】:

    • 没错,NSMutableArray 是一个相当不错的队列,尽管从前面移除并不是数组结构擅长的事情。即便如此,对于小型队列,性能无论如何都不是主要问题。我的一个朋友不久前在博客上写过这个话题……sg80bab.blogspot.com/2008/05/…
    【解决方案5】:

    是的,使用 NSMutableArray。 NSMutableArray 实际上是 implemented 作为 2-3 树;您通常不需要关心在任意索引处从 NSMutableArray 添加或删除对象的性能特征。

    【讨论】:

    • NSArray(和 NSMutableArray 的扩展)是一个类集群,这意味着它有几个私有实现,可以在幕后互换使用。你得到的通常取决于元素的数量。此外,Apple 可以随时更改任何给定实现的细节。但是,您说得对,它通常比标准数组灵活得多。
    【解决方案6】:

    re:Wolfcow -- 这是 Wolfcow 的 dequeue 方法的更正实现

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

    【讨论】:

      【解决方案7】:

      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)可以直接访问和操作内部存储阵列,但比使用类别要好得多。
      【解决方案8】:

      这是我的实现,希望对您有所帮助。

      有点简约,所以你必须通过在弹出时保存新磁头并丢弃旧磁头来跟踪磁头

      @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
      

      【讨论】:

        【解决方案9】:

        有什么特殊原因不能只使用 STL 队列吗? Objective C++ 是 C++ 的超集(只需使用 .mm 作为扩展名而不是 .m 以使用 Objective C++ 而不是 Objective C)。然后您可以使用 STL 或任何其他 C++ 代码。

        将 STL 队列/向量/列表等与 Objective C 对象一起使用的一个问题是它们通常不支持保留/释放/自动释放内存管理。这可以通过 C++ 智能指针容器类轻松解决,该容器类在构造时保留其 Objective C 对象,并在销毁时释放它。根据您在 STL 队列中放入的内容,这通常不是必需的。

        【讨论】:

        • 这似乎不是一个好主意...仅仅因为您可以做某事并不意味着您应该做某事。仅仅为了一个队列类而引入整个 STL 和 C++ 生态系统绝对是大材小用。
        • 实际上,自从发布后,这已经成为一个更好的主意。 Objective C++/ARC 意味着您可以将 STL 容器与 Objective C 对象指针一起使用,并且一切正常。 ARC 在 C++ 结构中自动为您处理内存管理。我通常还认为,作为一个更好的 C,C++ 使 Objective-C++ 通常比普通的 Objective C 成为更好的选择(例如,给出 enum 类之类的东西)。而且我非常怀疑添加 STL/C++ 是否会对任何现实世界应用程序的大小产生任何明显影响。
        【解决方案10】:

        使用 NSMutableArray。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-10-23
          • 1970-01-01
          • 2018-03-14
          • 2023-03-10
          • 1970-01-01
          • 2019-12-14
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多