Subclassing NSMutableString - objective-c

I know the first thing to do when one has a question with Obj-C is to read the Apple docs.
And I did. I read p.95-p.102 of the Cocoa Fundamentals Guide. So this is what I know about composite object creation from class clusters.
Subclassing of class clusters should be done if provided functions need to be modified.
Composite objects must override primitive methods of the super class. (And I also read that the primitive methods of the super class of the super class have to be overridden as well.)
So I applied what I know.
Here's my code...
#import <Foundation/Foundation.h>
#interface GOObjectKeyName : NSMutableString
{
NSMutableString *embeddedNSMutableString;
}
//+ GOObjectKeyName;
//- init;
//From NSString
- (NSUInteger) length;
// From NSString
- (unichar) characterAtIndex: (NSUInteger) index;
// From NSString
- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange;
// From NSMutableString
- (void)replaceCharactersInRange:(NSRange)aRange withString:(NSString *)aString;
//- (void) dealloc;
#end
#import "GOObjectKeyName.h"
#implementation GOObjectKeyName
/*
+ GOObjectKeyName
{
return [[[self alloc] init] autorelease];
}
- init
{
self = [super init];
if (self)
{
embeddedNSMutableString = [[NSMutableString allocWithZone: [self zone]] init];
}
return self;
}
*/
- (NSUInteger) length
{
return [embeddedNSMutableString length];
}
- (unichar) characterAtIndex: (NSUInteger) index
{
return [embeddedNSMutableString characterAtIndex: index];
}
- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange
{
[embeddedNSMutableString getCharacters: buffer range: aRange];
}
- (void)replaceCharactersInRange:(NSRange)aRange withString:(NSString *)aString
{
[embeddedNSMutableString replaceCharactersInRange: aRange withString: aString];
}
/*
- (void) dealloc
{
[embeddedNSMutableString release];
[super dealloc];
}
*/
#end
The commented methods are commented because I wasn't sure if they were necessary. I just wrote them down because I saw them in the example. And you also might see that it's basically another NSMutableString class. But don't worry, there is a bigger purpose to it, I just want to know that the basics are correct before I continue since this is my first time with composite objects.
And as you know... IT DOESN'T WORK!!
Please understand my frustration after hours of trial and error.
If someone could guide me here it would be a great relief.
Oh, and if you don't mind I have another small question.
The example on the apple docs describe the NSNumber analogy. But is NSNumber actually more efficient then using int, char, and other fundamental types?
Just curious.

It would certainly help if you would characterize the failure beyond "it doesn't work."
However, your -init method is commented out, so there's no way that embeddedMutableString is going to get created. And even if you uncomment it, you call +alloc to create embeddedMutableString, but you never initialize it.
After looking at the documentation, it seems that a subclass's -init method should look like this (warning: untested code):
-(id)init
{
if (self = [super init]) {
embeddedMutableString = [[NSMutableString alloc] init];
}
return self;
}
You will, of course, also have to provide a -dealloc that releases embeddedMutableString and calls [super dealloc].
As far as NSNumber, no, it's not more efficient than basic types like int but it does have other advantages. NSNumber is a class, so you can store instances of NSNumber in Cocoa's collection classes such as NSArray and NSSet. You can write them to property lists. NSDecimalNumber avoids the imprecision that comes from converting between decimal and binary notations. And so on...

To answer your NSNumber question, no, NSNumber is not more efficient than the C primitives. NSNumber is an encapsulation for the C primitives, essentialy allowing primitives to be inserted into objects like NSArray and NSSet. If you need that encapsulation ability, but want the speed of an actual int or float, it's simple enough to write your own NSNumber-like class that works for just one type.

Related

NSString alloc outside autorelease pool creates mysterious memory leak?

the following error is produced at runtime when manually allocating and releasing memory:
objc[10430]: Object 0x109014b60 of class __NSCFData autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
I searched the internet but usually these errors are caused by not releasing memory or using autoreleased shorthands... I cannot find any unreleased memory, nor am I using an autoreleased shorthand... Hoping you can help!
My code is:
#import "try.h"
#implementation try
- (try*)initWithSwing
{
self = [super init];
if (self) {
[self tellAboutSwing:YES];
}
return self;
}
- (void) tellAboutSwing: (BOOL) swing {
NSString *s = [[NSString alloc] initWithFormat: #"%# %#", #"Do we have a swing on our playground?", (swing ? #"Yes" : #"No")];
// cast to a char
const char *c = [s UTF8String];
[s release];
printf("%s", c);
}
- (void) dealloc {
printf( "Deallocing Playground\n" );
[super dealloc];
}
int main(void) {
try *t;
t = [[try alloc] initWithSwing];
[t release];
}
#end
Putting an autorelease pool inside the instance method fixes the issue, but that shouldn't be necessary with NSString alloc. While I am new to Objective-C I think I have grasped the concept of owning and releasing variables, yet I am having difficulty finding my error here.
Here's my .h file:
#include <Foundation/Foundation.h>
#interface try : NSObject
- (void) tellAboutSwing: (BOOL) swing;
#end
Help would be greatly appreciated :)
Regards,
Robert
IIRC -[NSString UTF8String] uses an autoreleased NSData to actually hold the bytes of the UTF-8 C string.
Update: Here's a simpler way using just C functions:
- (void) tellAboutSwing: (BOOL) swing {
printf("%s %s", "Do we have a swing on our playground?", (swing ? "Yes" : "No"));
}
You should just use an NSAutoreleasePool. It's normal to have one and at some point you won't find an alternative which is not creating autoreleased objects internally.
I think you should check out some tutorials and guides on memory management and the basics, otherwise you will have a hard time understaning Objective-C.
For example, there is a great course on iTunesU which is free and explains the basics of Objective-C.

Overriding methods in Objective-C

I am relatively new to Objective-C, and I have been thinking about the fact that all methods are, in effect, virtual. I created this little console program:
#import <Foundation/Foundation.h>
#interface BaseClass : NSObject
{
}
- (void) virtualMethod: (NSInteger) integer;
#end
#interface DerivedClass : BaseClass
{
}
- (void) virtualMethod: (NSString *) string;
#end
#implementation BaseClass
- (void) virtualMethod: (NSInteger) integer
{
NSLog(#"%ld", integer);
}
#end
#implementation DerivedClass
- (void) virtualMethod: (NSString *)string
{
NSLog(#"%#", string); // Program received signal: "EXC_BAD_ACCESS". -- as expected
}
#end
int main (int argc, const char * argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *arr = [NSArray arrayWithObjects: [BaseClass new], [DerivedClass new], nil];
for (int i = 0; i < arr.count; i++)
{
BaseClass *b = (BaseClass *)[arr objectAtIndex: i];
[b virtualMethod: i];
}
NSLog(#"\n\nTapos na!\n\n");
[pool drain];
return 0;
}
As expected, I got an EXC_BAD_ACCESS in the derived virtualMethod:, since after all, it doesn't take an integer, it takes an NSString * as parameter. The virtual mechanism is based on selectors, and doesn't seem to take signatures into account.
My question: is there something in the language that could prevent such an override with a different signature from happening? Some way to tell the compiler that virtualMethod: should always have the same signature, or to make the compiler issue some kind of hint, warning or error if the signature doesn't match?
I know that a good programmer always indicates the names of types a method should have, but that is a convention, not a language rule. I am asking about a compiler feature to prevent the problem from happening.
Well, there is... but you probably don't want to use it. There's a build setting in Xcode called "Strict Selector Matching" (which passes through to the compiler as -Wstrict-selector-match). This will warn you if the compiler finds two selectors that have different parameter or return types.
Unfortunately, the warning comes up even if the types are different, but compatible. As such, if you turn it on, you'll get a bunch of spurious warnings places that you wouldn't expect to be ambiguous. Feel free to try it out, though.
If you'd like to read more about it, Matt Gallagher wrote up a nice post about it. In the meantime, I'm afraid there's not a great solution here.
The current version of XCode gives you the following warning:
"Conflicting parameter types in implementation of '[method]': '[the type needed]' vs '[the type you wrongly chose]'"

NSMutableArray - force the array to hold specific object type only

Is there a way to force NSMutableArray to hold one specific object type only?
I have classes definitions as follow:
#interface Wheel:NSObject
{
int size;
float diameter;
}
#end
#interface Car:NSObject
{
NSString *model;
NSString *make;
NSMutableArray *wheels;
}
#end
How can I force wheels array to hold Wheel objects only with code? (and absolutely not other objects)
Update in 2015
This answer was first written in early 2011 and began:
What we really want is parametric polymorphism so you could declare, say, NSMutableArray<NSString>; but alas such is not available.
In 2015 Apple apparently changed this with the introduction of "lightweight generics" into Objective-C and now you can declare:
NSMutableArray<NSString *> *onlyStrings = [NSMutableArray new];
But all is not quite what it seems, notice the "lightweight"... Then notice that the initialisation part of the above declaration does not contain any generic notation. While Apple have introduced parametric collections, and adding a non-string directly to the above array, onlyStrings, as in say:
[onlyStrings addObject:#666]; // <- Warning: Incompatible pointer types...
will illicit the warning as indicated, the type security is barely skin deep. Consider the method:
- (void) push:(id)obj onto:(NSMutableArray *)array
{
[array addObject:obj];
}
and the code fragment in another method of the same class:
NSMutableArray<NSString *> *oops = [NSMutableArray new];
[self push:#"asda" onto:oops]; // add a string, fine
[self push:#42 onto:oops]; // add a number, no warnings...
What Apple have implemented is essentially a hinting system to assist with automatic inter-operation with Swift, which does have a flavour of type-safe generics. However on the Objective-C side, while the compiler provides some extra hints the system is "lightweight" and type-integrity is still ultimately down to the programmer - as is the Objective-C way.
So which should you use? The new lightweight/pseudo generics, or devise your own patterns for your code? There really is no right answer, figure out what makes sense in your scenario and use it.
For example: If you are targeting interoperation with Swift you should use the lightweight generics! However if the type integrity of a collection is important in your scenario then you could combine the lightweight generics with your own code on the Objective-C side which enforces the type integrity that Swift will on its side.
The Remainder of the 2011 Answer
As another option here is a quick general subclass of NSMutableArray which you init with the kind of object you want in your monomorphic array. This option does not give you static type-checking (in as much as you ever get it in Obj-C), you get runtime exceptions on inserting the wrong type, just as you get runtime exceptions for index out of bounds etc.
This is not thoroughly tested and assumes the documentation on overriding NSMutableArray is correct...
#interface MonomorphicArray : NSMutableArray
{
Class elementClass;
NSMutableArray *realArray;
}
- (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems;
- (id) initWithClass:(Class)element;
#end
And the implementation:
#implementation MonomorphicArray
- (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems
{
elementClass = element;
realArray = [NSMutableArray arrayWithCapacity:numItems];
return self;
}
- (id) initWithClass:(Class)element
{
elementClass = element;
realArray = [NSMutableArray new];
return self;
}
// override primitive NSMutableArray methods and enforce monomorphism
- (void) insertObject:(id)anObject atIndex:(NSUInteger)index
{
if ([anObject isKindOfClass:elementClass]) // allows subclasses, use isMemeberOfClass for exact match
{
[realArray insertObject:anObject atIndex:index];
}
else
{
NSException* myException = [NSException
exceptionWithName:#"InvalidAddObject"
reason:#"Added object has wrong type"
userInfo:nil];
#throw myException;
}
}
- (void) removeObjectAtIndex:(NSUInteger)index
{
[realArray removeObjectAtIndex:index];
}
// override primitive NSArray methods
- (NSUInteger) count
{
return [realArray count];
}
- (id) objectAtIndex:(NSUInteger)index
{
return [realArray objectAtIndex:index];
}
// block all the other init's (some could be supported)
static id NotSupported()
{
NSException* myException = [NSException
exceptionWithName:#"InvalidInitializer"
reason:#"Only initWithClass: and initWithClass:andCapacity: supported"
userInfo:nil];
#throw myException;
}
- (id)initWithArray:(NSArray *)anArray { return NotSupported(); }
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag { return NotSupported(); }
- (id)initWithContentsOfFile:(NSString *)aPath { return NotSupported(); }
- (id)initWithContentsOfURL:(NSURL *)aURL { return NotSupported(); }
- (id)initWithObjects:(id)firstObj, ... { return NotSupported(); }
- (id)initWithObjects:(const id *)objects count:(NSUInteger)count { return NotSupported(); }
#end
Use as:
MonomorphicArray *monoString = [[MonomorphicArray alloc] initWithClass:[NSString class] andCapacity:3];
[monoString addObject:#"A string"];
[monoString addObject:[NSNumber numberWithInt:42]]; // will throw
[monoString addObject:#"Another string"];
Since Xcode 7, generics are available in Objective-C.
You can declare a NSMutableArray as:
NSMutableArray <Wheel*> *wheels = [[NSMutableArray alloc] initWithArray:#[[Wheel new],[Wheel new]];
The compiler will give you a warning if you try to put non-Wheel objects in the array.
I could be wrong (I'm a noob), but I think, if you create a custom protocol and make sure the objects you are adding to the array follow the same protocol, then when you declare the array you use
NSArray<Protocol Name>
That should prevent objects being added that do not follow the said protocol.
as per i know.. before you added any object in wheels mutableArray, u have to add some check mark. Is the object which i am adding is class "wheel". if it is then add, other wise not.
Example:
if([id isClassOf:"Wheel"] == YES)
{
[array addObject:id)
}
Something like this. i dont remember the exact syntax.
I hope this will help (and work... :P )
Wheel.h file:
#protocol Wheel
#end
#interface Wheel : NSObject
#property ...
#end
Car.h file:
#import "Wheel.h"
#interface Car:NSObject
{
NSString *model;
NSString *make;
NSMutableArray<Wheel, Optional> *wheels;
}
#end
Car.m file:
#import "Car.h"
#implementation Car
-(id)init{
if (self=[super init]){
self.wheels = (NSMutableArray<Wheel,Optional>*)[NSMutableArray alloc]init];
}
return self;
}
#end
Xcode 7 allows you to define Arrays, Dictionaries, and even your own Classes as having generics. The array syntax is as follows:
NSArray<NSString*>* array = #[#"hello world"];
I don't believe there's any way to do it with NSMutableArray out of the box. You could probably enforce this by subclassing and overriding all the constructors and insertion methods, but it's probably not worth it. What are you hoping to achieve with this?
That's not possible; an NSArray (whether mutable or not) will hold any object type. What you can do is to create your own custom subclasses as already suggested by Jim. Alternatively, if you wanted to filter an array to remove objects that weren't of the type you want, then you could do:
- (void)removeObjectsFromArray:(NSMutableArray *)array otherThanOfType:(Class)type
{
int c = 0;
while(c < [array length])
{
NSObject *object = [array objectAtIndex:c];
if([object isKindOfClass:type])
c++;
else
[array removeObjectAtIndex:c];
}
}
...
[self removeObjectsFromArray:array otherThanOfType:[Car class]];
Or make other judgments based on the result of isKindOfClass:, e.g. to divide an array containing a mixture of Cars and Wheels into two arrays, each containing only one kind of object.
You can use the nsexception if you dont have the specific object.
for (int i = 0; i<items.count;i++) {
if([[items objectAtIndex:i] isKindOfClass:[Wheel class]])
{
// do something..!
}else{
[NSException raise:#"Invalid value" format:#"Format of %# is invalid", items];
// do whatever to handle or raise your exception.
}
}
Here's something I've done to avoid subclassing NSMutableArray: use a category. This way you can have the argument and return types you want. Note the naming convention: replace the word "object" in each of the methods you will use with the name of the element class. "objectAtIndex" becomes "wheelAtIndex" and so on. This way there's no name conflict. Very tidy.
typedef NSMutableArray WheelList;
#interface NSMutableArray (WheelList)
- (wheel *) wheelAtIndex: (NSUInteger) index;
- (void) addWheel: (wheel *) w;
#end
#implementation NSMutableArray (WheelList)
- (wheel *) wheelAtIndex: (NSUInteger) index
{
return (wheel *) [self objectAtIndex: index];
}
- (void) addWheel: (wheel *) w
{
[self addObject: w];
}
#end
#interface Car : NSObject
#property WheelList *wheels;
#end;
#implementation Car
#synthesize wheels;
- (id) init
{
if (self = [super init]) {
wheels = [[WheelList alloc] initWithCapacity: 4];
}
return self;
}
#end
protocol maybe a good idea:
#protocol Person <NSObject>
#end
#interface Person : NSObject <Person>
#end
to use:
NSArray<Person>* personArray;
There is one-header file project which allows this:
Objective-C-Generics
Usage:
Copy ObjectiveCGenerics.h to your project.
When defining a new class use the GENERICSABLE macro.
#import "ObjectiveCGenerics.h"
GENERICSABLE(MyClass)
#interface MyClass : NSObject<MyClass>
#property (nonatomic, strong) NSString* name;
#end
Now you can use generics with arrays and sets just as you normally do in Java, C#, etc.
Code:

Array Via Setter?

This is just a test to help me learn Objective-C, it uses NSMutableArray to add tire objects to an instance variable in a car object:
// INTERFACE
#interface CarBody : NSObject {
NSMutableArray *tires;
}
// Should this be (id *) it works but I was convinced it would be pointer?
- (void) addTire:(id)newTire;
#end
#interface TireSnow : NSObject {
}
#end
// IMPLEMENTATION
#implementation CarBody
- (void) addTire:(id)newTire {
[tires addObject:newTire];
// ** Release here or in main()?
}
- (id) init {
[super init];
tires = [[NSMutableArray alloc] init];
NSLog(#"_init: %#", NSStringFromClass([self class]));
return self;
}
- (void) dealloc {
NSLog(#"_deal: %#", NSStringFromClass([self class]));
[tires release];
[super dealloc];
}
#end
I do have a few questions ...
In the addTire method, is the (id) right, I thought it was going to be (id *)
Releasing the item I am adding to the array, should I do it inside the setter or in main() after I call it?
Am I allocating / releasing the NSMutableArray (tires) in the right place, it feels right?
Is there a way to do this with NSArray (as I only want 4 tires), I did try this but got mixed up trying to alloc the array and define its size.
thanks in advance for any help ...
gary
EDIT:
I am reading the memory management rules, but they do take some time to absorb and do require a certain level of understanding that is difficult to gain when starting out. What I am wondering about in this situation is where would I release the newSnowTire that I alloc in main. When I add it to the array in the setter does that create a new object in the array (thats my understanding) so my thinking was that I would need to release the instance I got from alloc?
// MAIN
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
CarBody *newCarBody_001;
TireSnow *newSnowTire_001;
newCarBody_001 = [[CarBody alloc] init];
newSnowTire_001 = [[TireSnow alloc] init];
[newCarBody_001 addTire:newSnowTire_001];
// Clean up
[newCarBody_001 release];
[newSnowTire_001 release];
[pool drain];
return 0;
}
EDIT_002:
Just added the code to generate all 4 tires with the tire release moved into the loop after the setter is called.
// CREATE TIRES
for(int loopCounter=0; loopCounter<4; loopCounter++) {
newSnowTire_001 = [[TireSnow alloc] init];
[newCarBody_001 addTire:newSnowTire_001];
[newSnowTire_001 release];
}
I just checked this and it is correct ...
NewSnowTire_001 (alloc) RetainCount = 1
NewSnowTire_001 (addTire) RetainCount = 2
NewSnowTire_001 (release) RetainCount = 1
NewSnowTire_001 Finally Released by dealloc method.
(id) or (TireSnow*) is similar, I had problems with understanding this in the beginning too. So basically an object is of a pointer type (kind of), but the id is already a pointer, so you don't need a * after it.
In main. Releasing should happen in the same place as the alloc/retain/copy.
Seems okay to me.
You can use [[NSMutableArray alloc] initWithCapacity:4]. This is only a hint to the array, it will automatically expand if you insert more items. Check [tires length] in the addTire method.
Your -init should look more like this:
-(id)init
{
if (self = [super init]) {
// init here
}
return self;
}
This allows self to be nil if something breaks in the init-chain.
You should use id (not id*). Objective-C do not have a concept of a root object as you have in for example Java, where java.lang.Object is the root class for any and all classes. Cocoa adds two root classes (classes without a super class) named NSObject, and less common NSProxy. id is a pointer to any object regardless of super class.
It is unfortunate that id, and also Class, are defined as a pointers, which means they are the only places where you should not add the '*' character when defining references. An unfortunate legacy from the old days.
Release in main, you should always release objects int he same scope that you create or retain them. The addTire: method is exceptionally god example of this, never release objects that has been handed to you as an argument. Only release objects handed to you as a result (And even then only from the alloc, new and copy method).
The allocation and release of the instance variable tires is a schoolbook example of where it should be done. I would expand the init to check for the super class result, as this though (Never trust super to always work, or even return the same instance):
- (id) init {
self = [super init];
if (self) {
tires = [[NSMutableArray alloc] init];
NSLog(#"_init: %#", NSStringFromClass([self class]));
}
return self;
}
You can use NSArray if you have access to all four tires from the start. Best way would probably be to require the tires in the init method. If that is not a possibility then you have nothing to gain from using an NSArray over a NSMutableArray.
The type id is defined like this (in objc.h):
typedef struct objc_object {
Class isa;
} *id;
So id is already a pointer to an object. An id* would be a pointer to a pointer.
As for where you should release the tire — there's nothing in the code you posted that shows a need to release it at all. That object never claims ownership of the tire, so it has no need to release it. If something claimed ownership of the tire somewhere else in your code, then that object has a responsibility to release its claim when it's finished.
This is explained in the Objective-C memory management rules. It's pretty short and a must-read.

How do I make and use a Queue in Objective-C?

I want to use a queue data structure in my Objective-C program. In C++ I'd use the STL queue. What is the equivalent data structure in Objective-C? How do I push/pop items?
Ben's version is a stack instead of a queue, so i tweaked it a bit:
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
Just import the .h file wherever you want to use your new methods, and call them like you would any other NSMutableArray methods.
I wouldn't say that using NSMutableArray is necessarily the best solution, particularly if you're adding methods with categories, due to the fragility they can cause if method names collide. For a quick-n-dirty queue, I'd use the methods to add and remove at the end of a mutable array. However, if you plan to reuse the queue, or if you want your code to be more readable and self-evident, a dedicated queue class is probably what you want.
Cocoa doesn't have one built in, but there are other options, and you don't have to write one from scratch either. For a true queue that only adds and removes from the ends, a circular buffer array is an extremely fast implementation. Check out CHDataStructures.framework, a library/framework in Objective-C that I've been working on. It has a variety of implementations of queues, as well as stacks, deques, sorted sets, etc. For your purposes, CHCircularBufferQueue is significantly faster (i.e. provable with benchmarks) and more readable (admittedly subjective) than using an NSMutableArray.
One big advantage of using a native Objective-C class instead of a C++ STL class is that it integrates seamlessly with Cocoa code, and works much better with encode/decode (serialization). It also works perfectly with garbage collection and fast enumeration (both present in 10.5+, but only the latter on iPhone) and you don't have to worry about what is an Objective-C object and what is a C++ object.
Lastly, although NSMutableArray is better than a standard C array when adding and removing from either end, it's also not the fastest solution for a queue. For most applications it is satisfactory, but if you need speed, a circular buffer (or in some cases a linked list optimized to keep cache lines hot) can easily trounce an NSMutableArray.
As far as I know, Objective-C does not provide a Queue data structure. Your best bet is to create an NSMutableArray, and then use [array lastObject], [array removeLastObject] to fetch the item, and [array insertObject:o atIndex:0]...
If you're doing this a lot, you might want to create an Objective-C category to extend the functionality of the NSMutableArray class. Categories allow you to dynamically add functions to existing classes (even the ones you don't have the source for) - you could make a queue one like this:
(NOTE: This code is actually for a stack, not a queue. See comments below)
#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
There's no real queue collections class, but NSMutableArray can be used for effectively the same thing. You can define a category to add pop/push methods as a convenience if you want.
Yes, use NSMutableArray. NSMutableArray is actually implemented as 2-3 tree; you typically need not concern yourself with the performance characteristics of adding or removing objects from NSMutableArray at arbitrary indices.
re:Wolfcow -- Here is a corrected implementation of Wolfcow's dequeue method
- (id)dequeue {
if ([self count] == 0) {
return nil;
}
id queueObject = [[[self objectAtIndex:0] retain] autorelease];
[self removeObjectAtIndex:0];
return queueObject;
}
The solutions that use a category on NSMutableArray are not true queues, because NSMutableArray exposes operations that are a superset of queues. For example, you should not be allowed to remove an item from the middle of a queue (as those category solutions still let you do). It is best to encapsulate functionality, a major principle of object oriented design.
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
this is my implementation, hope it helps.
Is kind of minimalistic, so you must keep the track of the head by saving the new head at pop and discarding the old head
#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
Is there some particular reason you cannot just use the STL queue? Objective C++ is a superset of C++ (just use .mm as the extension instead of .m to use Objective C++ instead of Objective C). Then you can use the STL or any other C++ code.
One issue of using the STL queue/vector/list etc with Objective C objects is that they do not typically support retain/release/autorelease memory management. This is easily worked around with a C++ Smart Pointer container class which retains its Objective C object when constructed and releases it when destroyed. Depending on what you are putting in the STL queue this is often not necessary.
Use NSMutableArray.