object inside NSMutableArray is not update immediately after updating - objective-c

I have a very strange error using NSMutableArray in cocos2d/xcode
In my code, I have a NSMutableArray containing object A
NSMutableArray *peasantArray;
and another NSMutableArray contain object B
NSMutableArray *treeArray;
in the scheduled update function, I called another function which is essentially the following functionality:
I would loop through all the object A inside the *peasantArray, and if any peasant object has a variable var == GameEntityCommandIdling, I would modify the object B in the second NSMutableArray *treeArray,
The issue is that I notice sometimes after I modified the object A inside the peasantArray, the modified variable (var) is being modified/updated inside the object A after by printing out the variable status in a scheduled fashion; but if I am to loop through the NSMutableArray *peasantArray again in the next schedule (1/30s), I will again find the object A with the older/un-updated variable (var), and this is causing my algorithm to be wrong,
However, if I loop through the NSMutableArray *peasantArray less than 1second, each time I would see the variable (var) for object A correctly as the updated variable value,
So is there a limit on how fast I can iterate over the NSMutableArray?
here are some piece of code that I basically just mentioned,
NSMutableArray *peasantArray;
NSMutableArray *treeArray;
.....
peasantArray = [[[NSMutableArray alloc] initWithCapacity:1]retain];
for(int i = 0; i < 1; i++)
{
Peasant *A = [[Peasant alloc] init];
[peasantArray addObject:A];
}
....
//inside the update()
for (int i = 0;i < [peasantArray count];i++)
{
Peasant *A = [peasantArray objectAtIndex:i];
if (A.status == something)
{
printf("A.status is something\n");
A.status = sometingelse;
...
//update other things not related to this NSMutableArray or object
}
}
.....
SO the error is that sometimes I will have multiple printf of "A.status is something", although only one of it should be printed,
I really appreciate any help,
Thanks,

So is there a limit on how fast I can iterate over the NSMutableArray?
Definitely no. That would be the stupidest implementation of an array I'd ever encountered. :)
The simplest explanation is usually the answer. You say the message is printed more than once. Well have you checked that maybe more than one Peasant has its status set to something?
If that's not the case, are you certain the status update is printed twice in the same frame, and not twice in two subsequent updates?
Earlier on you seemed to indicate that the effect of iterating over one array and modifying the other array's objects is invalidated somehow. That made me want to point out that if you have the same object in both arrays, modifying the object's properties in array A will also modify the properties of the same object contained in array B.
You may want to give breakpoints a(nother) try. The problem should be easy to locate when you step through the code.

Here you have a memory leak
for(int i = 0; i < 1; i++)
{
Peasant *A = [[Peasant alloc] init];
[peasantArray addObject:A];
}
you should release A after adding it to the array since the addObject adds a reference count to the object.
for(int i = 0; i < 1; i++)
{
Peasant *A = [[Peasant alloc] init];
[peasantArray addObject:A];
[A release];
}

Related

NSArray not deallocating in ARC Objective-C

I am trying to write a command line application in Objective-C for a university project. The project needs matrix manipulation so I have written a class to handle all the matrix methods (Addition and multiplication and such). My matrix methods look like this:
- (NSArray *)sumMatrices:(NSArray *)matrices
{
NSMutableArray *sum = [[NSMutableArray alloc] init];
NSInteger cols = [matrices[0][0] count];
NSInteger rows = [matrices[0] count];
for (int i = 0; i < rows; i++) {
NSMutableArray *row = [[NSMutableArray alloc] init];
for (int j = 0; j < cols; j++) {
CGFloat value = 0.0;
for (NSArray *array in matrices) {
value += [array[i][j] doubleValue];
}
[row addObject:[NSNumber numberWithDouble:value]];
}
[sum addObject:[row copy]];
row = nil;
}
return [sum copy];
}
However theres is a massive problem with this programme, I having used objective-c for iOS expect ARC to handle all my memory allocation and deallocation without a problem, however in this case the NSMutableArray 'sum' is never being deallocated, and because this method is being called in a loop which runs 10's of thousands of times (Modelling a double pendulum using RK4) the memory usage builds up and makes the program extremely slow.
Is there any reason this NSMutableArray isn't being deallocated once this method has returned?
Your problem is less about this code and more about the code surrounding it. Let's assume for a moment that your code around it looks like this:
NSArray *matricies; //Declared somewhere else;
NSMutableArray *results = [[NSMutableArray alloc] init];
for (int i=0; i < [matricies count] - 1; i++) {
for (int j=i+1; j < [matricies count]; i++) {
NSArray *sum = [self sumMatrices:#[matricies[i], matricies[j]]];
[results addObject:sum];
}
}
The actual operations that I'm performing are not particularly relevant to this example. The code pattern is. You'll notice I'm using a nested "tight" loop. Control never returns to the run loop until AFTER all calculations are complete. Without ARC, your memory would be freed as soon as the last release was performed, excluding autoreleased objects. With ARC, your memory is not freed until control is returned to the runloop, much the same way autoreleased objects used to. As a result, your code will appear to leak, until processing is complete and the system decides it should release your memory. If the CPU is perpetually under a heavy load, it may not clean up memory until it absolutely has to.
There are a few cleaver ways to use #autoreleasepool to help in this case, but that will make your code significantly slower. Additionally, Objective C is a fairly slow language for objects and method calls. If you are using this code heavily, you should convert it into C or C++ and manage the memory yourself.
without going into much detail you can try to use autoreleasepool
https://developer.apple.com/library/mac/documentation/cocoa/conceptual/memorymgmt/articles/mmAutoreleasePools.html
i would use copy if i want to preserve an array which gets modified but in your case do you really need it ?

Storing objects in an array in objective c

I'm trying to store 25 objects in an array
for (int iy=0; iy<5; iy++) {
for (int ix=0; ix<5; ix++) {
TerrainHex *myObject = [[TerrainHex alloc] initWithName:(#"grassHex instance 10000") width:mGameWidth height:mGameHeight indexX:ix indexY:iy];
myObject.myImage.y += 100;
[TerrainHexArray addObject:myObject];
[self addChild:(id)myObject.myImage];
}
}
NSLog(#"Terrain array: %u", [TerrainHexArray count]);
The log is coming back as zero though.
In the .h file I have
#property NSMutableArray *TerrainHexArray;
And in the .m file I have..
#synthesize TerrainHexArray;
I just tried what someone suggested below, which is..
NSMutableArray *TerrainHexArray = [[NSMutableArray] alloc] init];
But it's just giving me a warning saying expected identifier.
It's almost certain that TerrainHexArray does not exist when you're doing the addObject calls and the NSLog. You say you tried adding the alloc/init after someone suggested it, which indicates you don't understand object management in Objective-C.
I'd suggest you step back, find a book on Objective-C, and read at least the first few chapters (up through the discussion of alloc/init et al) before you attempt any more coding.
Incidentally, it's standard C++/Objective-C coding practice (except in Microsoft) to use identifiers with a leading lower case character for instance names, reserving leading caps for types/class names.
What is TerrainHexArray? It looks like a class name, not an instance of an array. If you create a mutable array, then you can add the items to the array.
NSMutableArray *hexArray = [[NSMutableArray] alloc] init];
for (int iy=0; iy<5; iy++) {
for (int ix=0; ix<5; ix++) {
TerrainHex *myObject = [[TerrainHex alloc] initWithName:(#"grassHex instance 10000") width:mGameWidth height:mGameHeight indexX:ix indexY:iy];
myObject.myImage.y += 100;
[hexArray addObject:myObject];
[self addChild:(id)myObject.myImage];
}
}
NSLog(#"Terrain array: %u", [hexArray count]);

objective-c changing array contents inside for loop

I have an issue that (I think) might have to do with scope, but I'm not sure. I'm trying to do something that I think should be simple, but I am getting a strange result, and I could truly use some advice. I would say I'm an early-objective-c programmer, but not a complete newb.
I have written a function in objective-c that I would like to use to change the key-names in a mutable array of mutable dictionary objects. So, I want to pass in a mutable array of mutable dictionary objects, and return the same mutable array with the same dictionary objects, but with some of the key-names changed. Make sense?
I have tried several log statements in this code, which seem to indicate that everything I'm doing is working, except when the for loop is finished executing (when I try to test the values in the temp array), the array appears to contain only the LAST element in the source array, repeated [source count] times. Normally, this would lead me to believe I'm not writing the new values correctly, or not reading them correctly, or even that my NSLog statements aren't showing me what I think they are. But might this be because of scope? Does the array not retain its changes outside of the for loop?
I have put a fair amount of time into this function, and I have exhausted my bag of tricks. Can anyone help out?
-(NSMutableArray *)renameKeysIn:(NSMutableArray*)source {
/*
// Pre:
// The source array is an array of dictionary items.
// This method renames some of the keys in the dictionary elements, to make sorting easier later.
// - "source" is input, method returns a mutable array
*/
// copy of the source array
NSMutableArray *temp = [source mutableCopy];
// a temporary dictionary object:
NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
// These arrays are the old field names and the new names
NSMutableArray *originalField = [NSMutableArray arrayWithObjects:#"text", #"created_at",nil];
NSMutableArray *replacedField = [NSMutableArray arrayWithObjects:#"title", #"pubDate", nil];
// loop through the whole array
for (int x =0; x<[temp count]; x++) {
// set the temp dictionary to current element
[dict setDictionary:[temp objectAtIndex:x]];
// loop through the number of keys (fields) we want to replace (created_at, text)... defined in the "originalField" array
for (int i=0; i<[originalField count]; i++)
{
// look through the NSDictionary item (fields in the key list)
// if a key name in the dictionary matches one of the ones to be replaced, then replace it with the new one
if ([dict objectForKey:[originalField objectAtIndex:i]] != nil) {
// add a new key/val pair: the new key *name*, and the old key *value*
[dict setObject:[dict objectForKey:[originalField objectAtIndex:i]]
forKey:[replacedField objectAtIndex:i]];
// remove the old key/value pair
[dict removeObjectForKey:[originalField objectAtIndex:i]];
}// end if dictionary item not null
}// end loop through keys (created_at, text)
[temp replaceObjectAtIndex:x withObject:dict];
}// end loop through array
// check array contents
for (int a=0; a<[temp count]; a++){
NSLog(#"Temp contents: ############ %#",[[temp objectAtIndex:a] objectForKey:#"pubDate"]);
}
return temp;
} // end METHOD
I think the issue is on the line with:
[dict setDictionary:[temp objectAtIndex:x]];
Since these things are almost all working in pointers (instead of copying contents), every element of your temp array will point to the dict dictionary, which is set to be whatever the latest key's dictionary is. I think setting the actual pointer will fix the issue.
dict = [temp objectAtIndex:x];

Need help with NSMutableArray solution

Im currently devising a solution for my object BuildingNode *tower which is held inside NSMutableArray *gameObjects, to attack EnemyNode *enemy objects also held inside the gameObjects array.
I have a proposed solution which occasionally causes my game to freeze (temporarily) as the solution employed is quite buggy.
My solution is that each tower object contains its own NSMutableArray *targets which is synthesized from the Tower Class. If an enemy comes into range or out of range of any given tower object, the corrosponding index of the EnemyNode *enemy object from the gameObjects array is saved as an NSNumber into the targets array, or alternatively if the enemy object is out of range, it is removed from the .targets array.
Basically the idea is that the targets array holds the indices of any enemy that is in scope of the tower.
The problem I seem to be facing is that because this tower.targets array is updated dynamically all the time, i believe that if i'm doing some sort of operation on a particular index of tower.targets that then is removed, i get this error:
-[BuildingNode distance]: unrecognized selector sent to instance 0x2dd140
Each BuildingNode *tower has different attacking alogrithms that use the tower.targets array for calling back the sorted/desired enemy.
For example, a random attack style will randomize a number between 0 & [tower.targets count] then I can create a pointer to gameObjects with the corresponding [tower.targets intValue].
Something like this:
EnemyNode *enemy = (EnemyNode *)[gameObjects objectAtIndex:[[tower.targets objectAtIndex:rand]intValue]];
So this will find a random enemy from the potential .targets array and then create a pointer object to an enemy.
I've put in many if statements to ensure that in the case of a .targets index being removed mid-sorting, the operation shouldnt go ahead, thus removing the game failure rate, but it still occurs occassionally.
Heres my code:
Please note that BuildingNode *tower == BuildingNode *build.
This is just a snippet of the iteration inside gameObjects.
//Potential Enemies (Indices) Add and Delete/
if (enemy.distance < build.atk_distance && !enemy.isExploding){
NSNumber *index = [NSNumber numberWithInt:[array indexOfObject:enemy]];
if(![build.targets containsObject:index]){
[build.targets addObject:index];
}
}
else {
NSNumber *index = [NSNumber numberWithInt:[array indexOfObject:enemy]];
if ([build.targets containsObject:index]){
[build.targets removeObject:index];
}
}
}
//Aiming// Nearest Algorithm.
//Will find the nearest enemy to the building
if (enemy.distance < build.atk_distance){
if (!build.isAttacking || build.atk_id == 0){
if ([build.targets count]){
if ([build.atk_style isEqualToString:#"near"]){
int l_dist;
for (int i = 0; i < [build.targets count]; i++){
//Grab the Enemy from Targets
if ([build.targets objectAtIndex:i]){
if([array objectAtIndex:[[build.targets objectAtIndex:i]intValue]]){
EnemyNode *temp = [array objectAtIndex:[[build.targets objectAtIndex:i]intValue]];
if (temp){
int c_dist = temp.distance;
if (!l_dist || c_dist < l_dist){
l_dist = c_dist;
build.atk_id = temp.uniqueID;
build.isAttacking = YES;
}
}
}
}
}
}
}}
Unrecognized selector sent = calling a method on an object that doesn't exist. In this case, you're calling the distance method/property getter on a BuildingNode object.
The only distance I see is on your temp object, which is retrieved by EnemyNode *temp = [array objectAtIndex:[[build.targets objectAtIndex:i]intValue]]; That indicates you are really pulling a BuildingNode object out of the array instead of an EnemyNode. Find out why that happens.

Purpose of "nil' at the end of Initializing Array in Objective-C

I have seen and done initialization of arrays and all put "nil" at the end of initialization but never question, why it is required to put there?
Plus if you are initializing your array in a loop, is it still necessary to put nil at the end of array? for example.
array = [[NSMutableArray alloc] init];
for (int i = 0 ; i < 10; i++)
{
[array addObject:#"1"];
}
// now this line is required or not after i exit the loop?
[array addObject:nil];
This concept is called nil-termination, and it's purpose is to provide a sentinel to the receiving function or method of where the variable argument list ends.