PDA

View Full Version : Make an array in Obj-C




gizabo
Apr 10, 2009, 06:27 PM
I want to make an array in obj c

i need it to store strings. For example:

*I like apple
*Objective C is cool
*My way or the Highway

Thanks,
Dan



lee1210
Apr 10, 2009, 06:37 PM
this is a bit ambiguous. Do you want the "C way", with an array of char * pointing to null terminated arrays of characters, or do you want to do things the "Cocoa way" and store an arbitrary number of NSString objects in an NS(Mutable)Array?

-Lee

gizabo
Apr 10, 2009, 06:43 PM
cocoa please :-)

gizabo
Apr 10, 2009, 07:33 PM
i just need to know how to make an array (in cocoa) that stores whole sentances (or strings)

lee1210
Apr 10, 2009, 07:58 PM
#import <Foundation/Foundation.h>

int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *myListOfStrings = [NSArray arrayWithObjects:@"My First String",@"My Second String",@"My Third String",@"My Fourth String",@"My Fifth String",nil];
for(int x=0;x<[myListOfStrings count];x++) {
NSLog(@"String %d is %@",x+1,[myListOfStrings objectAtIndex:x]);
}
[pool release];
return 0;
}


This is one way to do it. You cannot change myListOfStrings, though, it is fixed(unmutable). You could use NSMutableArray instead if needed.

@"abc" is the syntax for an NSString literal.

References:
NSString
http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html

NSArray:
http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/Reference/Reference.html

I was having trouble quickly finding the specific docs on NSString literals, so maybe someone else can post that.

-Lee

EDIT: I would far prefer the use of fast enumeration, but alas am still on 10.4, and wanted to make sure the code i posted compiled.

gizabo
Apr 10, 2009, 08:05 PM
Thanks for the reply again Lee! U have been a big help over the past few weeks


but im getting an error:


error: initializer element is not constant

lee1210
Apr 10, 2009, 08:08 PM
with the code i gave you? if not, post your code.

And you're welcome. =)

-Lee

gizabo
Apr 10, 2009, 08:10 PM
well this is the code in my appcontroller.m file



#import "AppController.h"

NSArray *myListOfStrings = [NSArray arrayWithObjects:@"Chullll ******",@"Big Kitchen? FAGOT!",@"Best sause ever. Cost $20",@"You should become a veggitarian",@"$400 BB GUNS!", @"Your TV sucks. Not HD!", @"Kar 98K with Double tap!", nil];

@implementation AppController

- (IBAction)buttonClicked:(id)sender
{
[response setStringValue:[NSString stringWithFormat:@"I have been clicked"]];
}

@end




lmao ignoore the strings... they are inside jokes with my friends

lee1210
Apr 10, 2009, 08:26 PM
What are you wanting to do here? The problem is that you are asking the compiler to initialize a particular variable at compile time with a non-constant expression that can only be evaluated at run-time. You also don't use the NSArray anywhere, so it's not doing much for you.

Also:
[NSString stringWithFormat:@"I have been clicked"]
is equivalent to:
@"I have been clicked"
except now there's a new autoreleased NSString that you don't really need.

Finally... hm... just use test data if you don't want people to see potentially offensive epithets, even if they are meant as harmless inside jokes? I am battling to ignore it, but there is a real possibility that such jokes may be at the expense of a group of which someone who is helping you is a member.

Back to the topic at hand... I would suggest initializing an NSArray in awakeFromNib, so it can be setup at runtime instead of compile time (you can't have an instance of an object at compile time).

-Lee

Edit: This is a decent reference for @"" NSString literals:
http://developer.apple.com/documentation/Cocoa/Conceptual/Strings/Articles/CreatingStrings.html

gizabo
Apr 10, 2009, 08:33 PM
I am sorry Lee, i did not mean to offend you in anyway whatsoever.
Im making this app for a friend of mine who has a friend who comments with everything by saying "******"

I really did not mean to offend you, and i am sorry. Honestly, i support that group and i think they should have every right that other people do. Im sorry though, and i shouldnt of wrote that

And ignoore

[NSString stringWithFormat:@"I have been clicked"]

i was just using that so i can see if the image i clicked actually works.

And im sorry, lol but i dont understand how to fix the problem... What code should i add?



(PS im really sorry about that though, i did not mean to offend you)

lee1210
Apr 10, 2009, 08:39 PM
I am not offended, i just wanted to point out that the potential is there.

This was the first hit i found on tutorials that include awakeFromNib:, so it may not be the best:
http://www.midgard-project.org/documentation/xcode_tutorial/

In any event, you need to make a method with the signature:
-(void)awakeFromNib

This will get called once, when the app starts. Here you can set up any of the data you need. You can declare your NSArray * as it is declared now, but do not initialize it there. Instead, put the code in awakeFromNib to initialize it. That way when the app starts, this array will be setup with the desired values, and can be used in subsequent method calls,etc..

-Lee

gizabo
Apr 10, 2009, 08:49 PM
bleh! I still dont get it! I have never been good at reading and gathering information :mad:

lee1210
Apr 10, 2009, 08:53 PM
In your AppController.m file, add a method:

-(void)awakeFromNib {
myListOfStrings = [NSArray arrayWithObjects:@"String 1",@"String 2",@"String 3",@"String 4",@"String 5", @"String 6", @"String 7", nil];
}


And change the original declartion of myListOfStrings to just:

NSArray *myListOfStrings;


-Lee

gizabo
Apr 10, 2009, 08:56 PM
Ahhh Lee, Lee, Lee... What would i do without you? :D


Thanks man! Cya around!

gizabo
Apr 10, 2009, 09:02 PM
Lol wait one more thing! Sorry!


[response setStringValue:[NSString stringWithFormat:@"%@", myListOfStrings[random() % 8 + 1;]];



Why doesnt that work? Im trying to change the label to one of my values in the array

eddietr
Apr 10, 2009, 09:13 PM
Why doesnt that work? Im trying to change the label to one of my values in the array

Well, before you go too far here, it seems as though you now have an NSArray declared in global scope which is very unusual for a Cocoa program and probably not what you wanted.

And I don't think you retained the array, either. (or are you using GC?)

If you need to keep the array around for a while, you probably want to create an instance variable in your AppController and then initialize that in awakeFromNib or just in your init as Lee as shown. Don't forget to retain it also (if you aren't using the garbage collector that is).

lee1210
Apr 10, 2009, 09:22 PM
Well, before you go too far here, it seems as though you now have an NSArray declared in global scope which is very unusual for a Cocoa program and probably not what you wanted.

And I don't think you retained the array, either.

If you need to keep the array around for a while, you probably want to create an instance variable in your AppController and then initialize that in awakeFromNib or just in your init as Lee as shown. Don't forget to retain it also.

ah, quite right... should be in the @interface in AppController.h. And eddietr is right... i picked a method that returned something autoreleased because in my example the scope was very small. It would be better to alloc and initWithObjects:, then retain.

In response to gizabo's last comment... you asked for the Cocoa way, and you got it =). You cannot access elements of an NSArray with the [] operator. Look at my original example, and the link I gave for NSArray to see how to properly get something out of the array at a particular index.

Also, did you call randomize() before using random()? Also, you add 1, so the range of indicies is 1 to 8. NSArrays are, like C-style arrays, 0-based.

I think you want something like:
[response setStringValue:[myListOfStrings objectAtIndex:(random()%8)]];

-Lee

gizabo
Apr 10, 2009, 10:11 PM
AHHH IM SO CONFUSED! sorry im just out of it today! Here is my code:


#import "AppController.h"

@implementation AppController

- (void)awakeFromNib {
NSArray *myListOfStrings = [NSArray arrayWithObjects:@"s1",@"s2",@"s3",@"s4",
@"s5", @"s6", @"s7", @"s8", nil];

}

- (IBAction)buttonClicked:(id)sender
{
[response setStringValue:[myListOfStrings objectAtIndex:(random()%8)]];
}

@end




#import <Cocoa/Cocoa.h>


@interface AppController : NSObject {

IBOutlet NSTextField *response;
NSArray *myListOfStrings;

}

- (IBAction)buttonClicked:(id)sender;

@end

lee1210
Apr 10, 2009, 10:22 PM
Does it work? It looks pretty good to me. The only thing is, in awakeFromNib, add:

[myListOfStrings retain];
randomize();


The issue here is that you'll never send it a release... probably not a big deal here, but it's normally critical that these "match". you could technically put it in the dealloc of the AppController... but this is likely not to be called anyway.

It also might be worth making something like:

-(NSString *)getRandomQuote
{
NSUInteger myPos = random()%[myListOfStrings count];
return [myListOfStrings objectAtIndex:myPos];
}


Then in buttonClicked instead of what's there now:

[response setStringValue:[self getRandomQuote]];


-Lee

gizabo
Apr 10, 2009, 10:26 PM
ok well here is my code

@implementation AppController

- (void)awakeFromNib {
NSArray *myListOfStrings = [NSArray arrayWithObjects:@"s1",@"s2",@"s3",@"s4",
@"s5", @"s6", @"s7", @"s8", nil];

[myListOfStrings retain];
randomize();

}

-(NSString *)getRandomQuote
{
return [myListOfStrings objectAtIndex:(random()%[myListOfStrings count])];
}

- (IBAction)buttonClicked:(id)sender
{
[response setStringValue:[self getRandomQuote]];
}

@end



and look at the warnings iget


warning: local declaration of 'myListOfStrings' hides instance variable
warning: implicit declaration of function 'randomize'

lee1210
Apr 10, 2009, 10:35 PM
OK... so #include <stdlib.h> for random() and randomize(), i had assumed incorrectly that was already done...

And change the first line of awakeFromNib to:

myListOfStrings = [NSArray arrayWithObjects:@"s1",@"s2",@"s3",@"s4",
@"s5", @"s6", @"s7", @"s8", nil];


I missed that the last time you posted your code. You don't want to declare a new variable, just assign the pointer to the existing ivar. In buttonClicked and getRandomQuote, they are looking at the uninitialized ivar called myListOfStrings, not the one local to awakeFromNib right now, hence the incorrect behavior.

-Lee

gizabo
Apr 10, 2009, 10:37 PM
thanks so much! but i feel lost about how this works... ill study this and look it up so i understand 100% how it works

THanks!

lee1210
Apr 10, 2009, 10:52 PM
So it does work now, you just don't know how?

I would look at the references I sent for each of the methods used:
For NSString the only thing that was used was @"" NSString literal notation to generate the strings to place in the array. Look at the String Programming Guide linked above as it discusses this notation a bit, and what sort of thing that actually gets you (an NSString * that points to an "immortal" NSString instance).

NSControl's setStringValue: method:
http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/ApplicationKit/Classes/NSControl_Class/Reference/Reference.html#//apple_ref/occ/instm/NSControl/setStringValue:

NSArray
initWithObjects:
http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSArray_Class/Reference/Reference.html#//apple_ref/occ/instm/NSArray/initWithObjects:

objectAtIndex:
http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSArray_Class/Reference/Reference.html#//apple_ref/occ/instm/NSArray/objectAtIndex:

I may have mistaken about the need for randomize(). It's always been in my head that it's needed, but maybe the BSD versions don't require it? Or you need to call srandom instead? You can read the manpage for random() by typing:
man random
from the terminal.

That's all of the things used, other than the connections in interface builder.

Since you're just getting started, the memory management guide is a must, too:
http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

-Lee

Liamg
May 14, 2009, 08:19 PM
Thanks Lee. Your replies were really helpful to me too.
Especially, my code was working, but I was getting the following unrecognized selector error on console from a tableView class:

2009-05-14 17:24:02.335 ImageInTable[25752:10b] Data is Two
2009-05-14 17:24:02.337 ImageInTable[25752:10b] Data is Two
...
2009-05-14 17:24:02.402 ImageInTable[25752:10b] *** -[NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x13ab00
2009-05-14 17:24:02.402 ImageInTable[25752:10b] *** -[NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x13ab00
...

Turned out to be a problem with retain count.
Your point on [myListOfStrings retain] fixed it.

I was curious as to why the code executed for (say) 10 rows and then generated the error.
What would have prompted Cocoa to release the array at a particular point?

lee1210
May 14, 2009, 09:46 PM
In looking for a reference on this, I was hoping to find something about the run loop/event loop that discussed this a bit, but instead came across this stack overflow thread:
http://stackoverflow.com/questions/6578/understanding-reference-counting-with-cocoa-objective-c

There is a pretty good explanation of what's going on there. Basically at some point (and it's not up to you) autoreleased things will be reclaimed. If you need something for a "long time" you need to say so by sending a retain. It looks like your memory got released before you were ready, then a new member of the NSString class cluster got created there and started getting your messages.

-Lee