ฉันจะสร้างและใช้คิวใน 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
ฉันเพิ่มบรรทัดแสดงความคิดเห็นที่จุดเริ่มต้นของ dequeue สำหรับผู้ที่ต้องการคืนค่า nil แทนที่จะเพิ่มข้อยกเว้นเมื่อพยายามยกเลิกคิวจากคิวว่าง IMO ตามพฤติกรรม NSMutableArray ของการเพิ่มข้อยกเว้นมีความสอดคล้องกับ Cocoa มากขึ้น หลังจากนั้นคุณสามารถโทรติดต่อ-countล่วงหน้าเพื่อตรวจสอบว่ามีวัตถุใดที่จะยกเลิกการจัดคิวหรือไม่ มันเป็นเรื่องของความชอบจริงๆ
Quinn Taylor

2
ฉันได้เพิ่มรหัสนี้ลงใน github repo แล้ว อย่าลังเลที่จะแยกหรือแจ้งให้เราทราบหากฉันมีสิ่งผิดปกติ: github.com/esromneb/ios-queue-object ขอบคุณ !!!
portforwardpodcast

2
ฉันขาดอะไรไปหรือการใช้งานนี้มีความซับซ้อน O (n) ในการจัดคิวหรือไม่ แย่มาก คุณจะดีขึ้นมากด้วยการใช้อาร์เรย์แบบวงกลม การใช้งานนี้อาจได้ผล แต่ความคิดของ O (n) dequeue นั้นเจ็บปวด
ThatGuy

11
@Wolfcow เมื่อคุณลบวัตถุออกจากดัชนี 0 ทุกวัตถุในอาร์เรย์จะเลื่อนลง ดังนั้นในการลบรายการเดียวจึงเป็น O (n) อาจใช้ได้ดีสำหรับคิวขนาดเล็กซึ่งอาจเป็น 99% ของเวลาในแอปพลิเคชันมือถือ แต่นี่จะเป็นวิธีแก้ปัญหาที่แย่มากสำหรับชุดข้อมูลขนาดใหญ่ในสถานการณ์ที่สำคัญต่อเวลา อีกครั้งไม่ใช่ว่าคุณจะพบว่าในสถานการณ์ C ส่วนใหญ่
ThatGuy

2
@ThatGuy ช้าไปหน่อย แต่ NSArray ถูกนำไปใช้กับบัฟเฟอร์แบบวงกลมดังนั้นรันไทม์จะไม่เป็น theta (N)
hhanesand

33

ฉันจะไม่บอกว่าการใช้ NSMutableArray จำเป็นต้องเป็นทางออกที่ดีที่สุดโดยเฉพาะอย่างยิ่งหากคุณกำลังเพิ่มวิธีการที่มีหมวดหมู่เนื่องจากความเปราะบางที่อาจทำให้เกิดขึ้นได้หากชื่อเมธอดชนกัน สำหรับคิวที่รวดเร็วและสกปรกฉันจะใช้วิธีการเพิ่มและลบที่ส่วนท้ายของอาร์เรย์ที่ไม่แน่นอน อย่างไรก็ตามหากคุณวางแผนที่จะใช้คิวซ้ำหรือถ้าคุณต้องการให้โค้ดของคุณอ่านง่ายและชัดเจนขึ้นคลาสคิวเฉพาะอาจเป็นสิ่งที่คุณต้องการ

โกโก้ไม่มีในตัว แต่มีทางเลือกอื่นและคุณไม่จำเป็นต้องเขียนตั้งแต่ต้น สำหรับคิวจริงที่เพิ่มและลบออกจากส่วนท้ายเท่านั้นอาร์เรย์บัฟเฟอร์แบบวงกลมเป็นการใช้งานที่รวดเร็วมาก ลองดูCHDataStructures.frameworkซึ่งเป็นไลบรารี / กรอบงานใน Objective-C ที่ฉันได้ทำ มันมีการใช้งานคิวที่หลากหลายเช่นเดียวกับสแต็ก, deques, ชุดที่เรียงลำดับ ฯลฯ สำหรับวัตถุประสงค์ของคุณCHCircularBufferQueueนั้นเร็วกว่าอย่างมาก (เช่นสามารถพิสูจน์ได้ด้วยการวัดประสิทธิภาพ) และอ่านได้ง่ายกว่า (ยอมรับในเชิงอัตนัย) มากกว่าการใช้ NSMutableArray

ข้อดีอย่างหนึ่งของการใช้คลาส Objective-C ดั้งเดิมแทนคลาส C ++ STL คือการรวมเข้ากับรหัสโกโก้อย่างราบรื่นและทำงานได้ดีขึ้นมากกับการเข้ารหัส / ถอดรหัส (การทำให้เป็นอนุกรม) นอกจากนี้ยังทำงานได้อย่างสมบูรณ์แบบกับการรวบรวมขยะและการแจงนับอย่างรวดเร็ว (ทั้งที่มีอยู่ใน 10.5+ แต่มีเพียงรุ่นหลังบน iPhone) และคุณไม่ต้องกังวลว่าวัตถุ Objective-C คืออะไรและวัตถุ C ++ คืออะไร

สุดท้ายนี้แม้ว่า NSMutableArray จะดีกว่าอาร์เรย์ C มาตรฐานเมื่อเพิ่มและลบจากปลายทั้งสองข้าง แต่ก็ไม่ใช่วิธีแก้ปัญหาที่เร็วที่สุดสำหรับคิว สำหรับแอปพลิเคชันส่วนใหญ่เป็นที่น่าพอใจ แต่ถ้าคุณต้องการความเร็วบัฟเฟอร์แบบวงกลม (หรือในบางกรณีรายการที่เชื่อมโยงได้รับการปรับให้เหมาะสมเพื่อให้สายแคชร้อน) สามารถทำให้ NSMutableArray ยากขึ้นได้อย่างง่ายดาย


2
ดีใจที่มีคนตอบกลับด้วยวิธีแก้ปัญหาแบบคิวจริง
Casebash

ลิงก์ทั้งหมดเสีย - ฉันจะหาเฟรมเวิร์กได้ที่ไหน ฉันได้อ่านสิ่งดีๆมากมายเกี่ยวกับเรื่องนี้ แต่ไม่พบรหัสจริง!
ม็อก

เฟรมเวิร์กฟังดูดี แต่ลิงก์ไปยัง SVN ยังใช้งานไม่ได้ มีโอกาสได้รับรหัสจากที่ไหน? แก้ไข: รับมาจากmac.softpedia.com/progDownload/…แต่ฉันไม่เห็นว่าเป็นเวอร์ชันปัจจุบันหรือไม่
Kay

Dave DeLong's Git repo cloneดูเหมือนจะเป็น repo แบบ go-to ในทุกวันนี้
Regexident

29

เท่าที่ฉันรู้ Objective-C ไม่มีโครงสร้างข้อมูลคิว ทางออกที่ดีที่สุดของคุณคือการสร้างNSMutableArrayและใช้[array lastObject], [array removeLastObject]สามารถดึงข้อมูลรายการและ[array insertObject:o atIndex:0]...

หากคุณทำสิ่งนี้NSMutableArrayบ่อยมากคุณอาจต้องการสร้างประเภท Objective-C เพื่อขยายการทำงานของคลาส หมวดหมู่ช่วยให้คุณสามารถเพิ่มฟังก์ชันลงในคลาสที่มีอยู่ได้แบบไดนามิก (แม้กระทั่งคลาสที่คุณไม่มีซอร์ส) - คุณสามารถสร้างคิวได้ดังนี้:

(หมายเหตุ: รหัสนี้ใช้สำหรับสแต็กไม่ใช่คิวดูความคิดเห็นด้านล่าง)

@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
คุณทราบหรือไม่ว่าคุณติดตั้งสแต็กที่นี่ไม่ใช่คิว
Jim Puls

อ๊ะ - ขอโทษ! - ดูการปรับเปลี่ยนของ Wolfcow ด้านล่าง
Ben Gotow

ฉันเห็นด้วยหากคุณแทนที่ "best bet" ด้วย "ตัวเลือกที่ง่ายที่สุด" :-) นักโครงสร้างข้อมูลและผู้หลงไหลในประสิทธิภาพต้องการคิวที่แท้จริง แต่ NSMutableArray สามารถยืนต่อคิวได้อย่างง่ายดาย
Quinn Taylor

3
+1 ให้เบ็นเพราะฉันต้องการโซลูชันสแต็กแม้ว่าจะมีการขอคิวก็ตาม :)
แลนด์

ทั้งหมดที่ฉันคิดได้คือความเจ็บปวด คุณกำลังแทรกออบเจ็กต์ที่จุดเริ่มต้นของอาร์เรย์จากนั้นคุณต้องคัดลอกทุกองค์ประกอบมากกว่า 1 ช่องว่างในแต่ละครั้งที่คุณแทรก ในกรณีนี้รายการที่เชื่อมโยงจะทำงานได้ดีกว่ามาก
TheM00s3

8

ไม่มีคลาสคอลเลกชันคิวจริง แต่ NSMutableArray สามารถใช้เพื่อสิ่งเดียวกันได้อย่างมีประสิทธิภาพ คุณสามารถกำหนดหมวดหมู่เพื่อเพิ่มวิธีการป๊อป / พุชได้ตามความสะดวกหากคุณต้องการ


จริงอยู่ที่ NSMutableArray สร้างคิวที่ดีทีเดียวแม้ว่าการลบออกจากด้านหน้าจะไม่ใช่สิ่งที่โครงสร้างอาร์เรย์มีความโดดเด่น ถึงกระนั้นสำหรับคิวขนาดเล็กประสิทธิภาพก็ไม่ได้เป็นปัญหาหลักอยู่ดี เพื่อนของฉันบล็อกเกี่ยวกับหัวข้อนี้ในขณะที่กลับมา ... sg80bab.blogspot.com/2008/05/…
Quinn Taylor

7

ใช่ใช้ NSMutableArray NSMutableArray ถูกนำมาใช้จริงเป็นต้นไม้ 2-3 ต้น โดยทั่วไปคุณไม่จำเป็นต้องกังวลเกี่ยวกับลักษณะการทำงานของการเพิ่มหรือลบวัตถุจาก NSMutableArray ที่ดัชนีโดยพลการ


1
NSArray (และ NSMutableArray โดยส่วนขยาย) เป็นคลัสเตอร์คลาสซึ่งหมายความว่ามีการใช้งานส่วนตัวหลายอย่างที่อาจใช้แทนกันได้ในเบื้องหลัง สิ่งที่คุณได้รับมักขึ้นอยู่กับจำนวนองค์ประกอบ นอกจากนี้ Apple ยังมีอิสระที่จะเปลี่ยนแปลงรายละเอียดของการใช้งานใด ๆ ได้ตลอดเวลา อย่างไรก็ตามคุณถูกต้องที่มักจะมีความยืดหยุ่นมากกว่าอาร์เรย์มาตรฐาน
Quinn Taylor

5

re: Wolfcow - นี่คือการปรับใช้วิธีการ dequeue ของ 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

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 que / vector / list etc กับ Objective C คือโดยทั่วไปแล้วพวกเขาไม่สนับสนุนการจัดการหน่วยความจำคง / release / autorelease สิ่งนี้สามารถทำงานได้อย่างง่ายดายกับคลาสคอนเทนเนอร์ C ++ Smart Pointer ซึ่งยังคงรักษาวัตถุ Objective C ไว้เมื่อสร้างและเผยแพร่เมื่อถูกทำลาย ขึ้นอยู่กับสิ่งที่คุณใส่ในคิว STL ซึ่งมักไม่จำเป็น


1
นี่ไม่ใช่ความคิดที่ดีจริงๆ ... เพียงเพราะคุณสามารถทำบางสิ่งได้ไม่ได้หมายความว่าคุณควรทำ การดึงระบบนิเวศ STL และ C ++ ทั้งหมดสำหรับคลาสคิวนั้นเกินความจำเป็นอย่างแน่นอน
เครื่องยนต์เอกซ์โทรปิก

3
อันที่จริงตั้งแต่โพสต์สิ่งนี้ก็กลายเป็นความคิดที่ดีขึ้นมาก Objective C ++ / ARC หมายความว่าคุณสามารถใช้คอนเทนเนอร์ STL กับตัวชี้วัตถุ Objective C และทุกอย่างก็ใช้ได้ ARC ดูแลการจัดการหน่วยความจำโดยอัตโนมัติสำหรับคุณภายในโครงสร้าง C ++ โดยทั่วไปฉันจะเถียงว่า C ++ เป็น C ที่ดีกว่ามากทำให้ Objective-C ++ เป็นตัวเลือกที่ดีกว่า Objective C ธรรมดา (ให้สิ่งต่างๆเช่น enum class เป็นต้น) และฉันสงสัยมากว่าการเพิ่ม STL / C ++ มีผลกระทบที่เห็นได้ชัดเจนกับขนาดของแอปในโลกแห่งความเป็นจริงใด ๆ
Peter N Lewis

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.