Làm cách nào để tạo và sử dụng Hàng đợi trong Objective-C?


107

Tôi muốn sử dụng cấu trúc dữ liệu hàng đợi trong chương trình Objective-C của mình. Trong C ++, tôi sẽ sử dụng hàng đợi STL. Cấu trúc dữ liệu tương đương trong Objective-C là gì? Làm cách nào để đẩy / bật các mục?

Câu trả lời:


153

Phiên bản của Ben là một ngăn xếp thay vì một hàng đợi, vì vậy tôi đã chỉnh sửa nó một chút:

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

Chỉ cần nhập tệp .h vào bất kỳ nơi nào bạn muốn sử dụng các phương thức mới của mình và gọi chúng như cách bạn thực hiện với bất kỳ phương thức NSMutableArray nào khác.

Chúc may mắn và Tiếp tục Mã hóa!


1
Tôi đã thêm một dòng bình luận ở đầu dequeue cho những người muốn trả về nil thay vì đưa ra một ngoại lệ khi cố gắng dequeue từ một hàng đợi trống. IMO, tuân theo hành vi của NSMutableArray về việc nâng cao một ngoại lệ phù hợp hơn với Cocoa. Sau cùng, bạn có thể gọi điện -counttrước để kiểm tra xem có bất kỳ đối tượng nào cần hủy không. Đó là vấn đề sở thích, thực sự.
Quinn Taylor

2
Tôi đã thêm mã này vào github repo. Vui lòng fork hoặc cho tôi biết nếu tôi gặp phải lỗi gì: github.com/esromneb/ios-queue-object Cảm ơn !!!
portetherpodcast

2
Tôi có thiếu cái gì đó không, hoặc việc triển khai này có độ phức tạp O (n) trên dequeue? Đó là khủng khiếp. Bạn sẽ tốt hơn nhiều với việc triển khai mảng tròn. việc triển khai này có thể hoạt động, nhưng ý tưởng về O (n) dequeue là rất khó.
ThatGuy

11
@Wolfcow, khi bạn xóa một đối tượng khỏi chỉ mục 0, mọi đối tượng trong mảng sẽ được chuyển xuống một đối tượng. Do đó, để loại bỏ một mục duy nhất, nó là O (n). Có lẽ tốt cho các hàng đợi nhỏ, có lẽ là 99% thời gian trong các ứng dụng di động, nhưng đây sẽ là một giải pháp tồi tệ cho các tập dữ liệu lớn trong các tình huống quan trọng về thời gian. Một lần nữa, không phải là bạn sẽ thấy điều đó trong hầu hết các tình huống C khách quan.
ThatGuy

2
@ThatGuy Hơi muộn, nhưng NSArray được triển khai với bộ đệm tròn, vì vậy thời gian chạy sẽ không là theta (N).
hhanesand

33

Tôi sẽ không nói rằng sử dụng NSMutableArray nhất thiết phải là giải pháp tốt nhất , đặc biệt nếu bạn đang thêm các phương thức với các danh mục, do tính dễ vỡ mà chúng có thể gây ra nếu các tên phương thức xung đột. Đối với một hàng đợi nhanh-n-dơ, tôi sẽ sử dụng các phương pháp để thêm và xóa ở cuối một mảng có thể thay đổi. Tuy nhiên, nếu bạn định sử dụng lại hàng đợi hoặc nếu bạn muốn mã của mình dễ đọc hơn và hiển thị rõ ràng hơn, một lớp hàng đợi chuyên dụng có thể là thứ bạn muốn.

Cocoa không được tích hợp sẵn, nhưng có những tùy chọn khác và bạn cũng không cần phải viết từ đầu. Đối với một hàng đợi thực sự chỉ thêm và bớt từ cuối, mảng đệm tròn là một cách triển khai cực kỳ nhanh chóng. Kiểm tra CHDataStructures.framework , một thư viện / khuôn khổ trong Objective-C mà tôi đang làm việc. Nó có nhiều cách triển khai hàng đợi, cũng như ngăn xếp, deques, tập hợp được sắp xếp, v.v. Đối với mục đích của bạn, CHCircularBufferQueue nhanh hơn đáng kể (nghĩa là có thể cung cấp với điểm chuẩn) và dễ đọc hơn (thừa nhận là chủ quan) so với việc sử dụng NSMutableArray.

Một lợi thế lớn của việc sử dụng lớp Objective-C gốc thay vì lớp C ++ STL là nó tích hợp liền mạch với mã Cocoa và hoạt động tốt hơn nhiều với mã hóa / giải mã (tuần tự hóa). Nó cũng hoạt động hoàn hảo với tính năng thu thập rác và liệt kê nhanh (cả hai đều có trong 10.5+, nhưng chỉ có trên iPhone) và bạn không phải lo lắng về đối tượng Objective-C và đối tượng C ++ là gì.

Cuối cùng, mặc dù NSMutableArray tốt hơn một mảng C tiêu chuẩn khi thêm và xóa từ hai đầu, nó cũng không phải là giải pháp nhanh nhất cho một hàng đợi. Đối với hầu hết các ứng dụng, điều đó là thỏa đáng, nhưng nếu bạn cần tốc độ, một bộ đệm tròn (hoặc trong một số trường hợp là danh sách liên kết được tối ưu hóa để giữ cho các dòng bộ nhớ cache luôn nóng) có thể dễ dàng gặp trục trặc NSMutableArray.


2
Rất vui khi ai đó thực sự trả lời với một giải pháp hàng đợi đúng
Casebash

Tất cả các liên kết bị hỏng - tôi lấy khuôn khổ đó ở đâu? Tôi đã đọc rất nhiều thứ hay về nó nhưng không thể tìm thấy mã thực sự!
amok 19/12/10

Khung công tác nghe có vẻ hứa hẹn nhưng các liên kết đến SVN vẫn bị phá vỡ. Có cơ hội nhận được mã ở đâu đó không? CHỈNH SỬA: Đã nhận được nó từ mac.softpedia.com/progDownload/… nhưng tôi không thể biết đây có phải là phiên bản hiện tại hay không
Kay

Bản sao repo Git của Dave DeLong dường như là bản repo đáng dùng nhất hiện nay.
Regexident

29

Theo như tôi biết, Objective-C không cung cấp cấu trúc dữ liệu hàng đợi. Đặt cược tốt nhất của bạn là tạo NSMutableArray, sau đó sử dụng [array lastObject], [array removeLastObject]để tìm nạp mục và [array insertObject:o atIndex:0]...

Nếu bạn đang làm điều này nhiều, bạn có thể muốn tạo một danh mục Objective-C để mở rộng chức năng của NSMutableArraylớp. Danh mục cho phép bạn thêm động các chức năng vào các lớp hiện có (ngay cả những lớp bạn không có nguồn) - bạn có thể tạo một hàng đợi như thế này:

(LƯU Ý: Mã này thực sự dành cho ngăn xếp, không phải hàng đợi. Xem nhận xét bên dưới)

@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
Bạn có biết rằng bạn đã triển khai một ngăn xếp ở đây, không phải một hàng đợi?
Jim Puls

Ahh - xin lỗi! - xem các sửa đổi của Wolfcow bên dưới.
Ben Gotow

Tôi đồng ý nếu bạn thay thế "đặt cược tốt nhất" bằng "tùy chọn đơn giản nhất". :-) Những người theo chủ nghĩa thuần túy cấu trúc dữ liệu và những người ám ảnh về hiệu suất sẽ thích một hàng đợi thực sự, nhưng một NSMutableArray có thể dễ dàng đứng trong một hàng đợi.
Quinn Taylor

3
+1 đến ben vì tôi muốn có một giải pháp ngăn xếp mặc dù một hàng đợi được hỏi cho :)
whitneyland

Tất cả những gì tôi có thể nghĩ đến là nỗi đau. Bạn đang chèn một đối tượng vào đầu một mảng, sau đó bạn cần sao chép mọi phần tử trên 1 khoảng trắng mỗi khi bạn chèn. Trong trường hợp này, danh sách liên kết sẽ hoạt động tốt hơn nhiều.
TheM00s3

8

Không có lớp tập hợp hàng đợi thực sự, nhưng NSMutableArray có thể được sử dụng cho hiệu quả tương tự. Bạn có thể xác định một danh mục để thêm các phương thức pop / push nếu bạn muốn.


Đúng, NSMutableArray tạo ra một hàng đợi khá tốt, mặc dù loại bỏ từ phía trước không phải là thứ mà cấu trúc mảng vượt trội. Mặc dù vậy, đối với các hàng đợi nhỏ, hiệu suất không phải là mối quan tâm lớn. Một người bạn của tôi đã viết blog về chủ đề này một thời gian trước ... sg80bab.blogspot.com/2008/05/…
Quinn Taylor

7

Có, sử dụng NSMutableArray. NSMutableArray thực sự được thực hiện dưới dạng cây 2-3; bạn thường không cần quan tâm đến các đặc điểm hiệu suất của việc thêm hoặc xóa các đối tượng khỏi NSMutableArray tại các chỉ mục tùy ý.


1
NSArray (và NSMutableArray theo phần mở rộng) là một cụm lớp, có nghĩa là nó có một số triển khai riêng có thể được sử dụng thay thế cho nhau sau hậu trường. Một trong những bạn nhận được thường phụ thuộc vào số lượng các phần tử. Ngoài ra, Apple có thể tự do thay đổi các chi tiết của bất kỳ triển khai nhất định vào bất kỳ lúc nào. Tuy nhiên, bạn đúng là nó thường linh hoạt hơn nhiều so với một mảng tiêu chuẩn.
Quinn Taylor

5

re: Wolfcow - Đây là cách triển khai đã sửa chữa của phương pháp dequeue của Wolfcow

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

4

Các giải pháp sử dụng một danh mục trên NSMutableArraykhông phải là hàng đợi đúng, bởi vìNSMutableArray hiển thị các hoạt động là một tập hợp các hàng đợi. Ví dụ: bạn không được phép xóa một mục ở giữa hàng đợi (vì các giải pháp danh mục đó vẫn cho phép bạn làm). Tốt nhất là đóng gói chức năng, một nguyên tắc chính của thiết kế hướng đối tượng.

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

người ta có thể tranh luận rằng với một số kỹ thuật nhất định (tức là KVC), mảng lưu trữ nội bộ có thể được truy cập và thao tác trực tiếp, nhưng tốt hơn nhiều so với việc sử dụng một danh mục.
vikingosegundo

3

đây là cách thực hiện của tôi, hy vọng nó sẽ hữu ích.

Là một loại tối giản, vì vậy bạn phải theo dõi phần đầu bằng cách lưu phần đầu mới và loại bỏ phần đầu cũ

@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ó lý do cụ thể nào khiến bạn không thể chỉ sử dụng hàng đợi STL không? Objective C ++ là một tập hợp siêu của C ++ (chỉ cần sử dụng .mm làm phần mở rộng thay vì .m để sử dụng Objective C ++ thay vì Objective C). Sau đó, bạn có thể sử dụng STL hoặc bất kỳ mã C ++ nào khác.

Một vấn đề khi sử dụng hàng đợi STL / vector / danh sách, v.v. với các đối tượng Objective C là chúng thường không hỗ trợ quản lý bộ nhớ giữ lại / phát hành / tự động khôi phục. Điều này có thể dễ dàng làm được với một lớp chứa C ++ Smart Pointer, lớp này giữ lại đối tượng Objective C của nó khi được xây dựng và giải phóng nó khi bị phá hủy. Tùy thuộc vào những gì bạn đang đặt trong hàng đợi STL, điều này thường không cần thiết.


1
Đây thực sự không phải là một ý kiến ​​hay ... chỉ vì bạn có thể làm điều gì đó không có nghĩa là bạn nên làm. Việc kéo toàn bộ hệ sinh thái STL và C ++ chỉ cho một lớp hàng đợi chắc chắn là quá mức cần thiết.
động cơ hướng ngoại vào

3
Trên thực tế, kể từ khi nó được đăng, điều này đã trở thành một ý tưởng tốt hơn nhiều. Objective C ++ / ARC có nghĩa là bạn có thể sử dụng vùng chứa STL với con trỏ đối tượng Objective C và tất cả đều hoạt động. ARC sẽ tự động quản lý bộ nhớ cho bạn trong cấu trúc C ++. Nói chung, tôi cũng tranh luận rằng C ++, là một C tốt hơn nhiều, làm cho Objective-C ++ nói chung trở thành lựa chọn tốt hơn so với Objective C thuần túy (ví dụ như những thứ như lớp enum). Và tôi rất nghi ngờ việc thêm STL / C ++ có bất kỳ tác động đáng chú ý nào đến kích thước của bất kỳ ứng dụng nào trong thế giới thực hay không.
Peter N Lewis

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.