Question about NSMutableArray - objective-c

I currently try to fill and NSMutableArray with something like this:
deck = [[NSMutableArray alloc] initWithCapacity:52];
for (int suit = 0; suit <= 3; suit++) {
for (int value = 1; value <= 13; value++) {
ANormalCard *card = [[ANormalCard alloc] initWithSuit:suit value:value];
[deck addObject:card];
[card autorelease];
}
}
Now the problem is when I go over the array, only the last object I create is 52x in the array. Any idea what I am doing wrong ?
Edit:
the -initWithSuit looks like this:
- (id) initWithSuit:(int)suit value:(int)val {
if ((self = [super init])) {
theSuit = suit;
theValue = val;
}
return self;
}
I'm using NSEnumerator * enumerator = [deck objectEnumerator]; and a while loop to iterate over the array.

This code snippet looks perfectly fine. The problem is likely in your -initWithSuit:value: method — I'd check whether you're accidentally initializing the cards incorrectly.
Edit: The init method just posted by the asker looks fine as well. What is the exact output you're seeing that leads you to believe that only the last object has been added 52 times? How exactly are you examining the array? (Your comment says you're using an NSEnumerator, but can you edit your question to include the snippet you're using for that?

Related

Declaring a pointer in a loop

everyone. My "engrish" is not very good but I hope you will understand my problem.
Let's say I have a code like this
-(id) init
{
if ( self = [super init] )
{
deck = [[NSMutableArray alloc] initWithCapacity:52];
Card *newCard = [[Card alloc] init];
for ( int suit = 0; suit < 4; suit++ )
for ( int rank = 0; rank < 13; rank++ )
{
newCard.suit = suit;
newCard.rank = rank;
[deck addObject:newCard];
}
[newCard release];
}
return self;
}
I'm feeling like I have a mistake in the aforementioned code. I want to create 52 different objects in nested loops and add every object in array. But I suspect that I'll have 52 same objects and in array will be 52 pointers that point to the same address, right?
What happens if I make like this.
-(id) init
{
if ( self = [super init] )
{
deck = [[NSMutableArray alloc] initWithCapacity:52];
for ( int suit = 0; suit < 4; suit++ )
for ( int rank = 0; rank < 13; rank++ )
{
Card *newCard = [[Card alloc] init]; // I guess every time newCard
newCard.suit = suit; // created, it will point to
newCard.rank = rank; // another chunk of memory,
[deck addObject:newCard]; // right?
[newCard release] // Should I release newCard every time?
}
}
return self;
}
So which way I can create 52 different cards?
Thank you. If you need more explanations, ask me.
First question: Your second code sniplet produces 52 individual objects which you add to the array.
The first sniplet, as you already suggested yourself, produces just one object which you add to the array. As it is one object only all the members of the array will carry the same values for .suit and .rank.
Als suggested already, when you nslog the objects then you see at least their address in memory. When the address is identical then it is the identical object.
Second question:
Yes, you should release it unless you use ARC. addObject will automatically retain each added object and release it upon removal from the array. So be careful when you fetch the object from the array later and intend to use it further. Then you may have to retain it again.
Alternative to your -correct- code you can autorelease the object using:
Card *newCard = [[[Card alloc] init] autorelease]; // I guess every time newCard
newCard.suit = suit; // created, it will point to
newCard.rank = rank; // another chunk of memory,
[deck addObject:newCard]; // right?
//[newCard release]; // not required, autoreleased
(However, a semicolon is missing following the release statement. )

iOS adding object to an array from another array base on 3rd array value

EDIT: All array used in my project are NSMutableArray class
An overview of what I want to do is from my selectClueView, user can select a number from 3-10. Which represent the number of clue they will play. It will then generate list of random number between 0 and the objectArray.count and add the NSNumber into another array known as dataArray. Everything is working fine including prepareForSegue which transfer SelectClueViewController.dataArray to GamePageViewController.clueToSelect
However, I am stuck with loading data into the new array ds, from an array that hold all the object allDataObject. I am fairly new to iOS and because I had a working function in c#, I tried to replicate it in objective-C, unfortunately it seems that I can't replicate it fully.
In short, I'm trying to add data from allDataObject array into ds array with NSNumber values from cluesToSelect array.
Below are the coding which are used. Any help to fix the issue would be much appreciated. If there are any more information that I should give, please let me know.
SelectClueViewController.m
- (IBAction)onGoPress:(id)sender {
[self chooseNumber];
NSLog(#"Array got %d numbers",dataArray.count);
}
-(void)chooseNumber
{
[dataArray removeAllObjects];
maxCount = [numberOfClues.text intValue];
int count = 0;
do {
NSInteger rdmNumber = arc4random()%objectArray.count;
if (![dataArray containsObject:[NSNumber numberWithInt:rdmNumber]])
{
NSNumber* number = [NSNumber numberWithInt:rdmNumber];
[dataArray addObject:number];
count++;
NSLog(#"random no - %d",rdmNumber);
}
} while (count < maxCount);
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"sendNumber"]) {
GamePageViewController *gpViewController = [segue destinationViewController];
gpViewController.cluesToSelect = self.dataArray;
NSLog(#"Success");
}
}
GamePageViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
daoDS = [[ClueDataDAO alloc]init];
self.allDataObject = daoDS.PopulateDataSource;
NSLog(#"%d",cluesToSelect.count);
[self fillDataSample];
//for keyboard
self.answer.delegate = self;
}
-(void)fillDataSample
{
int count = 0;
do {
// [self.ds addObject:[allDataObject objectAtIndex:[[cluesToSelect objectAtIndex:count]intValue] ]];
ds = [[NSMutableArray alloc]init];
currentClueData = [[ClueData alloc]init];
int firstIndex = [[cluesToSelect objectAtIndex:count]intValue];
currentClueData = [allDataObject objectAtIndex:firstIndex];
[ds addObject:currentClueData];
count++;
} while (count < cluesToSelect.count);
NSLog(#"ds got %d object",ds.count);
}
EDIT:
I am now able to make it add in object into the ds array, unfortunately it only add once. Can someone look at my fillDataSample function?
In the line you point at at the beginning :
[self.ds addObject:[allDataObject objectAtIndex:[cluesToSelect objectAtIndex:count]]];
Just a wild guess, but [cluesToSelect objectAtIndex:count] returns an object. You're trying to pass that to another objectAtIndex:, which takes an int as argument. I'm guessing the error could come from that. You could try using .intValue if it's a number.
Edit :
Here's something more readable, considering cluesToSelect and allDataObject contain only NSNumbers :
int firstIndex = [[cluesToSelect objectAtIndex:count] intValue];
int secondIndex = [[allDataObject objectAtIndex: firstIndex] intValue];
[self.ds addObject:secondIndex];
As per our discussion,
Kindly check this one:
[self.ds addObject:[allDataObject objectAtIndex:[[cluesToSelect objectAtIndex:count] intValue]]];
will do your work.
The reason as to why it didn't work for my new method of loading object from allClueData to a class object currentClueData to ds was because I did not nil and alloc init the class object after adding it to ds. The object were unable to overwrite their own value like they do in other language. (which was probably why I'm wrecking my brain for doing it in objective C) But after adding in nil the object and casting alloc and init, its working great now. thanks all :)
-(void)fillDataSample
{
int count = 0;
currentClueData = [[ClueData alloc]init];
ds = [[NSMutableArray alloc]init];
do {
// [self.ds addObject:[allDataObject objectAtIndex:[[cluesToSelect objectAtIndex:count]intValue] ]];
int firstIndex = [[cluesToSelect objectAtIndex:count]intValue];
currentClueData = [allDataObject objectAtIndex:firstIndex];
[ds addObject:currentClueData];
currentClueData =nil;
currentClueData = [[ClueData alloc]init];
count++;
} while (count < cluesToSelect.count);
NSLog(#"ds got %d object",ds.count);
}

How to manage int when it's incremented inside another loop?

I have a simple loop with an int counter that gets incremented inside a while loop when a special case exists. My question is simply - how should I manage memory inside this function with regards to the int specifically? I've been using NSNumber almost exclusively and what little time I've spent with int seems to make me think I'm not doing releasing it correctly.
Any other improvements are also welcome but I'm very interested in the int question
- (NSArray *)parseJson:(NSArray *) items
{
NSMutableArray* hats = [[NSMutableArray alloc] init];
NSEnumerator *enumerator = [items objectEnumerator];
NSDictionary* item;
int counterz = 0;
while (item = (NSDictionary*)[enumerator nextObject]) {
Hat* hat = [[Hat alloc] init];
hat.addr = [item objectForKey:#"Address"];
BOOL* hasHat = [item objectForKey:#"HasHat"];
if ([hasHat boolValue]) {
if (counterz < 10) {
[hats addObject:hat];
counterz++;
}
}
}
return hats;
}
Thank you in advance!
You don't need to release a "normal" (i.e.: non-object based) int - it'll happily life out its (brief, tragic) life on the stack until it falls out of scope.
You've got a couple unnecessary things and some memory leaks...
- (NSArray *)parseJson:(NSArray *) items {
NSMutableArray *hats = [NSMutableArray array];
int counter = 0;
for (NSDictionary *item in items) {
Hat *hat = [[Hat alloc] init];
[hat setAddr:[item objectForKey:#"Address"]];
BOOL hasHat = [[item objectForKey:#"HasHat"] boolValue];
if (hasHat && counter < 10) {
[hats addObject:hat];
counter++;
}
[hat release];
}
return hats;
}
And heck, once you reach a counter of 10, you could break out of the loop, because you're never going to do anything useful once 10 is reached.
Some other comments:
The name of the method is wrong. Nothing about this method has to do with parsing JSON. At best you're interpreting an array of dictionaries that happened to originate from a JSON string, but there's nothing about the nature of this code that says "this is parsing JSON".
-[NSDictionary objectForKey:] returns an object. A BOOL is not an object, it's a primitive (like an int or char). Appending * to the type does not make it an object either. :)
Since the method name does not begin with new or alloc and does not contain the word copy, you're supposed to return an autoreleased object from it. The method in the question was returning an owned object (+1 retain count), since you invoked alloc, but never autorelease. Using the convenience constructor +array fixes this.
In your loop, you allocated a Hat object, but never released it. This is a classic memory leak.

How to change this so that it returns arrays

The following code works perfectly and shows the correct output:
- (void)viewDidLoad {
[super viewDidLoad];
[self expand_combinations:#"abcd" arg2:#"" arg3:3];
}
-(void) expand_combinations: (NSString *) remaining_string arg2:(NSString *)s arg3:(int) remain_depth
{
if(remain_depth==0)
{
printf("%s\n",[s UTF8String]);
return;
}
NSString *str = [[NSString alloc] initWithString:s];
for(int k=0; k < [remaining_string length]; ++k)
{
str = [s stringByAppendingString:[[remaining_string substringFromIndex:k] substringToIndex:1]];
[self expand_combinations:[remaining_string substringFromIndex:k+1] arg2:str arg3:remain_depth - 1];
}
return;
}
However, instead of outputting the results, I want to return them to an NSArray. How can this code be changed to do that? I need to use the information that this function generates in other parts of my program.
There are several things that you need to change in your code.
First - consider changing the name of your method to something more legible and meaningful than -expand_combinations:arg2:arg3.
Second - you have a memory leak. You don't need to set allocate memory and initialize str with the string s, because you change its value right away in the loop without releasing the old value.
Third - take a look at NSMutableArray. At the beginning of the method, create an array with [NSMutableArray array], and at every line that you have printf, instead, add the string to the array. Then return it.
basicaly you have:
create mutable array in viewDidLoad before [self expand_combinations ...
add aditional parameter (mutable array) to expand_combinations
populate array in expand_combinations

Declare Dynamic Array

How can I declare dynamic array? For example:
int k=5;
I want to have an array like below:
int myArray[k];
if i read the question right.. (unlikely at this point)
NSMutableArray *myArray = [[NSMutableArray alloc] initWithCapacity:k];
Sometimes true arrays (not NSArray) are really needed. See for example indexPathWithIndexes:length: in NSIndexPath, it take array of uintegers as parameter. For array allocation you should use the following approach:
NSUInteger *arr = (NSUInteger*)malloc(elementsCount * sizeof(NSUInteger) );
arr[0] = 100;
free(arr);
In Objective-C, the standard way to do this is to use the NSMutableArray class. This is a container that can hold any object (note that int is not an object! You'll have to wrap your integers in NSNumber.) Quick example:
NSMutableArray* someIntegers = [[NSMutableArray alloc] initWithCapacity:1];
[someIntegers addObject:[NSNumber numberWithInt:2]];
//I've added one thing to my array.
[someIntegers addObject:[NSNumber numberWithInt:4]];
//See how I can put more objects in than my capacity allows?
//The array will automatically expand if needed.
//The array now contains 2 (at index 0) and 4 (at index 1)
int secondInteger = [[someIntegers objectAtIndex:1] intValue];
//Retrieving an item. -intValue is needed because I stored it as NSNumber,
//which was necessary, because NSMutableArray holds objects, not primitives.
Well in my book it's ok to use VLAs in Objective-C.
So something like
int foo = 10;
int bar[foo];
is allowed. Of course this is not a dynamic array as in automatically adjusting its size. But if you only need a native array on the stack that's fine.
You can use Objetive-C++.
First rename your class like this: MyClass.mm the ".mm" extension tells Xcode that this clas is a Objetive-C++ class, not a Objetive-C class.
then you can use dynamics C++ arrays like this:
int *pixels = new int[self.view.size.width];
for (int offset = 0; offset = self.view.size.width; offset++) {
pixeles[offset] = rawData[offset];
}
then you can pass "pixels" in a method:
Scan *myScan = [[Scan alloc] initWhithArray:pixels];
the method "initWithScan" is declared like this:
-(id)initWithArray:int[]pixels;
the "initWithScan" implementation is like this:
-(id)initWithScan:int[]pixels {
if (self = [super init]) {
for (int i = 0; i < self.myView.size.width; i++) {
NSLog(#"Pixel: %i", pixels[i];
}
}
return self;
}
I hoppe this was useful.