Objective-C'de bir Kuyruğu nasıl oluşturabilir ve kullanabilirim?


107

Objective-C programımda bir kuyruk veri yapısı kullanmak istiyorum. C ++ 'da STL kuyruğunu kullanırım. Objective-C'deki eşdeğer veri yapısı nedir? Öğeleri nasıl itebilirim / çıkarabilirim?

Yanıtlar:


153

Ben'in sürümü bir kuyruk yerine bir yığın, bu yüzden biraz değiştirdim:

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

Yeni yöntemlerinizi kullanmak istediğiniz her yerde .h dosyasını içe aktarın ve diğer NSMutableArray yöntemlerinde olduğu gibi onları çağırın.

İyi şanslar ve Kodlamaya Devam Et!


1
Kuyruktan çıkarma işleminin başlangıcına, boş bir kuyruktan kuyruktan çıkarmaya çalışırken bir istisna oluşturmak yerine sıfır döndürmek isteyenler için yorumlanmış bir satır ekledim. IMO, NSMutableArray bir istisna oluşturma davranışını izleyerek Cocoa ile daha tutarlıdır. Sonuçta, -countkuyruğundan çıkarılacak herhangi bir nesne olup olmadığını kontrol etmek için önceden arayabilirsiniz . Bu bir tercih meselesi, gerçekten.
Quinn Taylor

2
Bu kodu bir github deposuna ekledim. Bir sorunla karşılaşırsam çatallaşmaktan veya bana haber vermekten çekinmeyin: github.com/esromneb/ios-queue-object Teşekkürler !!!
portforwardpodcast

2
Bir şeyi mi kaçırıyorum, yoksa bu gerçeklemenin sıradaki O (n) karmaşıklığı var mı? Bu korkunç. Dairesel bir dizi uygulamasıyla çok daha iyi durumda olursunuz. bu uygulama işe yarayabilir, ancak O (n) dequeue fikri acı vericidir.
ThatGuy

11
@Wolfcow, 0 dizininden bir nesneyi kaldırdığınızda, dizideki her nesne bir aşağı kaydırılır. Bu nedenle, tek bir öğeyi kaldırmak için O (n) 'dir. Mobil uygulamalarda muhtemelen zamanın% 99'u olan küçük kuyruklar için muhtemelen iyidir, ancak bu, kritik kritik durumlarda büyük veri kümeleri için korkunç bir çözüm olacaktır. Yine, bunu çoğu nesnel C durumunda bulacağınızdan değil.
ThatGuy

2
@ThatGuy Biraz geç, ancak NSArray dairesel bir tamponla uygulandı, bu nedenle çalışma zamanı theta (N) olmayacak.
hhanesand

33

NSMutableArray kullanmanın mutlaka en iyi çözüm olduğunu söyleyemem , özellikle de yöntem isimleri çakışırsa neden olabilecekleri kırılganlık nedeniyle kategoriler içeren yöntemler ekliyorsanız. Hızlı ve kirli bir kuyruk için, değiştirilebilir bir dizinin sonuna ekleme ve çıkarma yöntemlerini kullanırdım. Bununla birlikte, kuyruğu yeniden kullanmayı planlıyorsanız veya kodunuzun daha okunaklı ve açık olmasını istiyorsanız, muhtemelen istediğiniz şey özel bir kuyruk sınıfıdır.

Cocoa'da yerleşik bir tane yok, ancak başka seçenekler de var ve sıfırdan bir tane de yazmanız gerekmiyor. Yalnızca uçlardan eklenen ve çıkaran gerçek bir kuyruk için, dairesel bir tampon dizisi son derece hızlı bir uygulamadır. Check out CHDataStructures.framework , ben üzerinde çalıştığımızı zaten Objective-C bir kütüphane / çerçeve. Bir dizi kuyruk uygulamasının yanı sıra yığınlar, sıralar, sıralı kümeler vb. De vardır. Sizin amaçlarınız için, CHCircularBufferQueue NSMutableArray kullanmaktan önemli ölçüde daha hızlıdır (yani kıyaslamalarla kanıtlanabilir) ve daha okunabilirdir (kuşkusuz özneldir).

C ++ STL sınıfı yerine yerel bir Objective-C sınıfı kullanmanın büyük bir avantajı, Cocoa koduyla sorunsuz bir şekilde entegre olması ve kodlama / kod çözme (serileştirme) ile çok daha iyi çalışmasıdır. Aynı zamanda çöp toplama ve hızlı numaralandırma ile mükemmel çalışır (her ikisi de 10.5+ sürümünde bulunur, ancak yalnızca iPhone'da bulunur) ve Objective-C nesnesinin ne olduğu ve C ++ nesnesinin ne olduğu konusunda endişelenmenize gerek yoktur.

Son olarak, NSMutableArray, her iki uçtan ekleyip çıkarırken standart bir C dizisinden daha iyi olmasına rağmen, aynı zamanda bir kuyruk için en hızlı çözüm değildir. Çoğu uygulama için tatmin edicidir, ancak hıza ihtiyacınız varsa, dairesel bir arabellek (veya bazı durumlarda önbellek hatlarını sıcak tutmak için optimize edilmiş bir bağlantılı liste) bir NSMutableArray'i kolayca geçebilir.


2
Birinin gerçekten gerçek bir kuyruk çözümüyle yanıt vermesine sevindim
Casebash

Tüm bağlantılar bozuk - bu çerçeveyi nereden edinebilirim? Bununla ilgili çok güzel şeyler okudum ama gerçek kodu bulamıyorum!
amok

Çerçeve umut verici görünüyor ancak SVN ile olan bağlantılar hala bozuk. Kodu bir yerden alma şansınız var mı? DÜZENLEME: Mac.softpedia.com/progDownload/… adresinden aldım ancak bunun mevcut sürüm olup olmadığını göremiyorum
Kay

Dave DeLong'un Git repo klonu , bu günlerde gidilecek depo gibi görünüyor.
Regexident

29

Bildiğim kadarıyla, Objective-C bir Queue veri yapısı sağlamıyor. Yapabileceğiniz en iyi şey bir yaratmaktır NSMutableArrayve sonra kullanmak [array lastObject], [array removeLastObject]öğeyi almak için, ve [array insertObject:o atIndex:0]...

Bunu çok yapıyorsanız, NSMutableArraysınıfın işlevselliğini genişletmek için bir Objective-C kategorisi oluşturmak isteyebilirsiniz . Kategoriler, mevcut sınıflara (kaynağına sahip olmadığınız sınıflara bile) dinamik olarak işlevler eklemenize izin verir - şunun gibi bir sıra oluşturabilirsiniz:

(NOT: Bu kod aslında bir sıra için değil, yığın içindir. Aşağıdaki yorumlara bakın)

@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
Kuyruk değil, burada bir yığın uyguladığınızın farkında mısınız?
Jim Puls

Ahh - üzgünüm! - aşağıdaki Wolfcow değişikliklerine bakın.
Ben Gotow

"En iyi bahis" i "en basit seçenek" ile değiştirirseniz kabul ederim. :-) Veri yapısı sadeliği ve performans tutkunları gerçek bir kuyruğu tercih ederler, ancak NSMutableArray kolayca bir kuyruğa girebilir.
Quinn Taylor

3
Ben'e

Tek düşünebildiğim acı. Bir dizinin başlangıcına bir nesne ekliyorsunuz, daha sonra her eklediğinizde her öğeyi 1 boşluğa kopyalamanız gerekir. Bu durumda bağlantılı bir liste çok daha iyi performans gösterecektir.
TheM00s3

8

Gerçek kuyruk toplama sınıfı yoktur, ancak NSMutableArray aynı şey için etkili bir şekilde kullanılabilir. İsterseniz kolaylık olması açısından pop / push yöntemleri eklemek için bir kategori tanımlayabilirsiniz .


Doğru, NSMutableArray oldukça iyi bir kuyruk oluşturuyor, ancak önden çıkarmak bir dizi yapısının üstün olduğu bir şey değil. Yine de, küçük kuyruklar için performans zaten önemli bir sorun değil. Bir arkadaşım bir süre önce bu konu hakkında blog yazdı ... sg80bab.blogspot.com/2008/05/…
Quinn Taylor

7

Evet, NSMutableArray kullanın. NSMutableArray aslında bir uygulamaya 2-3 ağaç olarak; NSMutableArray'den rasgele indekslerde nesne eklemenin veya kaldırmanın performans özellikleriyle genellikle ilgilenmeniz gerekmez.


1
NSArray (ve uzantıya göre NSMutableArray) bir sınıf kümesidir, yani perde arkasında birbirinin yerine kullanılabilecek birkaç özel uygulamaya sahiptir. Alacağınız genellikle elementlerin sayısına bağlıdır. Ayrıca Apple, herhangi bir uygulamanın ayrıntılarını istediği zaman değiştirmekte özgürdür. Ancak, bunun genellikle standart bir diziden çok daha esnek olduğu konusunda haklısınız.
Quinn Taylor

5

re: Wolfcow - İşte Wolfcow'un dequeue yönteminin düzeltilmiş bir uygulaması

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

4

Üzerinde bir kategori kullanan çözümler NSMutableArraygerçek kuyruklar değildir, çünkü kuyrukların NSMutableArraybir üst kümesi olan işlemleri ortaya çıkarır. Örneğin, kuyruğun ortasından bir öğeyi kaldırmanıza izin verilmemelidir (bu kategori çözümleri hala yapmanıza izin verdiği için). En iyisi, nesne yönelimli tasarımın temel ilkelerinden biri olan işlevselliği kapsüllemek.

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

belirli tekniklerle (yani KVC) dahili depolama dizisine doğrudan erişilebileceği ve değiştirilebileceği, ancak bir kategori kullanmaktan çok daha iyi olduğu iddia edilebilir.
vikingosegundo

3

bu benim uygulamam, umarım yardımcı olur.

Biraz minimalisttir, bu yüzden yeni kafayı popta kaydedip eski kafayı atarak kafanın izini sürmelisiniz.

@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 kuyruğunu kullanmamanın belirli bir nedeni var mı? Objective C ++, C ++ 'ın bir üst kümesidir (yalnızca, Objective C yerine Objective C ++ kullanmak için .m yerine uzantı olarak .mm kullanın). Daha sonra STL veya başka bir C ++ kodunu kullanabilirsiniz.

STL kuyruğunu / vektörünü / listesini vb. Objective C nesneleriyle kullanmanın bir sorunu, genellikle bellek yönetimini tut / serbest bırak / otomatik bırakmayı desteklememeleridir. Bu, inşa edildiğinde Objective C nesnesini tutan ve yok edildiğinde serbest bırakan bir C ++ Smart Pointer konteyner sınıfı ile kolayca çözülür. STL kuyruğuna ne koyduğunuza bağlı olarak bu genellikle gerekli değildir.


1
Bu gerçekten sırf ... iyi bir fikir gibi görünmüyor olabilir sen gerektiği anlamına gelmez bir şey yap. Tüm STL ve C ++ ekosistemini sadece bir kuyruk sınıfı için çekmek kesinlikle aşırı derecede abartıdır.
extropic-engine

3
Aslında, yayınlandığından beri bu çok daha iyi bir fikir haline geldi. Objective C ++ / ARC, STL konteynerlerini Objective C nesne işaretçileri ile kullanabileceğiniz anlamına gelir ve hepsi çalışır. ARC, C ++ yapıları içerisinde bellek yönetimini sizin için otomatik olarak halleder. Genel olarak, C ++ 'nın çok daha iyi bir C olması, Objective-C ++' yı genel olarak düz Objective C'den daha iyi bir seçim haline getirdiğini (örneğin, enum sınıfı gibi şeyler vermek) iddia ediyorum. Ve STL / C ++ eklemenin herhangi bir gerçek dünya uygulamasının boyutu üzerinde gözle görülür bir etkisi olduğundan şüpheliyim.
Peter N Lewis

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.