I am getting a BAD_ACCESS in my for in loop - objective-c

I have the following block of code
-(void) spawnEnemies:(ccTime)dt
{
static float lastSpawn;
lastSpawn += dt;
if (lastSpawn > 0.75) {
for (NSDictionary *enemyData in levelData.enemies) {
if ([[enemyData valueForKey:#"spawnCount"] intValue] < [[enemyData valueForKey:#"quantity"] intValue]) {
// reset spawn timer
lastSpawn = 0;
// init enemy
id enemy = [NSClassFromString([enemyData valueForKey:#"className"]) enemyInit];
[enemy startAnimating];
[enemy setSpawnPosition];
[enemy moveToSpriteInit:playerSprite];
[self addChild:enemy];
[enemy release];
NSNumber *spawnCount = [enemyData valueForKey:#"spawnCount"];
[enemyData setValue:[NSNumber numberWithInt:([spawnCount intValue] + 1)] forKey:#"spawnCount"];
}
}
}
}
I would like to know why this line:
[enemyData setValue:[NSNumber numberWithInt:([spawnCount intValue] + 1)] forKey:#"spawnCount"];
Is throwing a bad access error? I am trying to update the spawn count for each enemy each iteration. Do I need to make a rebuild the levelData.enemies array within the for loop and reassign the new one with the updated spawn count values to levelData.enemies once the for loop is completed?

I'm dumb, I just realized I did not update the for loop object type from NSDictionary to NSMutableDictionary, can't change values in an NSDict, duh!

Related

Quicksort Error when sorting large array

I get an error message when I try to sort an array of one hundred thousand ints(I've tested the sort and it works for fifty thousand):
warning: could not load any Objective-C class information. This will significantly reduce the quality of type information available.
I also have the compare method iteratively and the same error appears
Why is this error message happening?
Is it possible to sort that list and if so how to fix it?
-(void) quickSort{
return [self quickSort:0 pivot:[self.toSort count]-1 right: [self.toSort count]-1];
}
-(void) quickSort:(int) left pivot:(int) pivot right:(int) right {
if(left < right){
pivot = [self compareToPivot:[[self.toSort objectAtIndex:right] intValue] start:left finish:right position:left];
[self quickSort:pivot+1 pivot:pivot right:right];
[self quickSort:left pivot:pivot right:pivot-1];
}
}
-(int) compareToPivot:(int) pivot start:(int)start finish:(int)finish position:(int)pos{
if(pos == finish){
[self switchNums:start second:finish];
return start;
}
if([[self.toSort objectAtIndex:pos] intValue] <= pivot){
[self switchNums:pos second:start];
return [self compareToPivot:pivot start:start+1 finish:finish position:pos+1];
}
return [self compareToPivot:pivot start:start finish:finish position:pos+1];
}
-(void) switchNums:(int)first second:(int)second{
id temp = [self.toSort objectAtIndex:second];
[self.toSort replaceObjectAtIndex:second withObject:[self.toSort objectAtIndex:first]];
[self.toSort replaceObjectAtIndex:first withObject:temp];
}
Iterative Compare and this runs fine:
-(int) compareToPivot:(int) pivot start:(int)start finish:(int)finish position:(int)pos{
while(pos != finish){
if([[self.toSort objectAtIndex:pos] intValue] <= pivot){
[self switchNums:pos second:start];
start+=1;
}
pos++;
}
[self switchNums:start second:finish];
return start;
}
While Quicksort itself is considered a recursive algorithm, the partition step (your comparetoPivot method) is not meant for recursion. And your implementation of this method is not only recursive, but it also is O(N) as far as stack space is concerned. You only recurse with one less element than before with each recursion into compareToPivot. Blowing up (running out stack) at around 100K elements sounds is to be expected as the default stack is around 500KB.
I used your question as an excuse to practice my Obj-C, which I'm a bit rusty on. But here's the implementation of recursive Quicksort straight out of the classic Algorithms (Cormen et. al.) book and adapted for your toSort array. I haven't tested it all out yet, but you can certainly give it a whirl.
-(int)partition: (NSMutableArray*)arr pivot:(int)pivot right:(int)right
{
int x = [[arr objectAtIndex: pivot] intValue];
int i = pivot;
int j = right;
while (true)
{
while ([[arr objectAtIndex: j] intValue] > x)
{
j--;
}
while ([[arr objectAtIndex: i] intValue] < x)
{
i++;
}
if (i < j) {
id objI = [arr objectAtIndex: i];
id objJ = [arr objectAtIndex: j];
[arr replaceObjectAtIndex:i withObject:objJ];
[arr replaceObjectAtIndex:j withObject:objI];
j--;
i++;
}
else
{
return j;
}
}
}
-(void)quickSort
{
int len = (int)[self.toSort count];
[self quicksort:self.toSort left:0 right:(len-1)];
}
-(void)quicksort: (NSMutableArray*)arr left:(int)left right:(int)right
{
if (left< right)
{
int q = [self partition:arr pivot:left right:right];
[self quicksort: arr left:left right:q];
[self quicksort: arr left:(q+1) right:right];
}
}

Deleting one object at a time every 5 seconds Cocos2d

I have a problem with my cocos2d game. I am trying to delete a projectile shot by an enemy every 5 seconds (each projectile is supposed to have a lifetime of 5 seconds), but I cannot figure out how to do it. I get the error of
Assertion failure in -[CCTimer initWithTarget:selector:interval:]
Here is my code:
-(void)projectileShooting:(ccTime)dt
{
[self schedule:#selector(projectileShooting:) interval:2.5];
projcount++;
if([proj count] <= 15 ){
if(enemy1.position.y < 320){
v = ccp(player.position.x,player.position.y);
for(CCSprite *enemies in enemy){
CCSprite * projectilebullet = [CCSprite spriteWithFile:#"Projectile.png"];
[proj addObject:projectilebullet];
[self addChild:projectilebullet];
CGPoint MyVector = ccpSub(enemies.position,player.position );
MyVector = ccpNormalize(MyVector);
MyVector = ccpMult(MyVector, enemies.contentSize.width/2);
MyVector = ccpMult(MyVector,-1);
projectilebullet.position = ccpAdd(enemies.position, MyVector);
for(CCSprite *projectile in proj){
[self schedule:#selector (deleteprojectile:projectile:) interval:5];
}
}
}
}
for(CCSprite *enem2 in enemytwo)
{
if( [proj count] <= 15){
CCSprite * projectilebull = [CCSprite spriteWithFile:#"Projectile.png"];
CGPoint MyVector = ccpSub(enem2.position,player.position );
MyVector = ccpNormalize(MyVector);
MyVector = ccpMult(MyVector, enem2.contentSize.width/2+10);
MyVector = ccpMult(MyVector,-1);
projectilebull.position = ccpAdd(enem2.position, MyVector);
[self addChild:projectilebull];
[proj addObject:projectilebull];
for(CCSprite *projectile in proj){
}
}
}
}
-(void)deleteprojectile:(CCSprite *)protime:(ccTime)dt{
NSMutableArray *timepro = [[NSMutableArray alloc]init];
[timepro addObject:protime];
for(CCSprite *objecttime in timepro){
[proj removeObject:objecttime];
[self removeChild:objecttime cleanup:YES];
}
}
It's a bit of a hack but this is what I use in my program, until I find a more elegant solution. I have a method in my game layer that I call to remove a node from its parent, like so:
-(void)removeNode:(CCNode*)node {
[node removeFromParentAndCleanup:YES];
}
And when I want to schedule a node for deletion after a delay, I call it like this:
[self performSelector:#selector(removeNode:) withObject:node afterDelay:delay];
Simple, and it works.
Change argument name in your selector to protime instead of projectile. The selector must match the signature defined in you object's class definition.
Your selector was not defined properly and probably the Timer is checking if the object implements the given selector.
I did not have time to test it so thanks to #RamyAlZuhouri for confirming.

Simple Code works in Debug, not in Release

So this isn't making sense at all. I have an extension method to NSMutableArray to move an item from one index to another. It's a fairly simple method and it works flawlessly when I compile my app in Debug Configuration. However, if I compile the app in Release Configuration, it crashes if I move from an item down (from index > to index). The crash isn't a an index out of bounds error. My local variable are being messed up and I have no idea why. Here's the entire method:
- (void) moveObjectAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex{
if (fromIndex == toIndex) return;
if (fromIndex >= self.count) return;
if (toIndex >= self.count) toIndex = self.count - 1; //toIndex too large, assume a move to end
id movingObject = [self objectAtIndex:fromIndex];
if (fromIndex < toIndex){
for (int i = fromIndex; i <= toIndex; i++){
[self replaceObjectAtIndex:i withObject:(i == toIndex) ? movingObject : [self objectAtIndex:i + 1]];
}
} else {
//The problem occurs in this block (though the crash doesn't always occur here)
id cObject;
id prevObject;
for (int i = toIndex; i <= fromIndex; i++){
//usually on the last loop, my "prevObject" become 'messed up' after the following line:
cObject = [self objectAtIndex:i];
[self replaceObjectAtIndex:i withObject:(i == toIndex) ? movingObject : prevObject];
prevObject = cObject;
}
}
}
I can't step through the code since it's a release build, but I've NSLogged the variables at each step through the loop. Usually, on the last loop the prevObject var is assigned some random variable when cObject = [self objectAtIndex:i]; completes. Sometimes it's set to nil but often it's some other random variable in my code. If it's nil the code crashes when I try to replace the object in the array. Otherwise, it crashes later on when I try to access the array and receive back the wrong object.
Does anyone have any idea what's going on? I mean, the problem is occurring in 4 lines of code, which I've been over a hundred times.
Ok, just as the problem/crash made no sense, so also the fix doesn't make any sense at all. I had started to just randomly change up the code to see if anything would work. Moving the line: prevObject = cObject to the very beginning of the loop fixed the problem. Doing this didn't change the logic at all... Nada... shouldn't have made a difference. And yet, it did. Whoever said programming is logical? Here's the code that works:
- (void) moveObjectAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex{
if (fromIndex == toIndex) return;
if (fromIndex >= self.count) return;
if (toIndex >= self.count) toIndex = self.count - 1; //toIndex too large, assume a move to end
id movingObject = [self objectAtIndex:fromIndex];
if (fromIndex < toIndex){
for (int i = fromIndex; i <= toIndex; i++){
[self replaceObjectAtIndex:i withObject:(i == toIndex) ? movingObject : [self objectAtIndex:i + 1]];
}
} else {
id cObject = nil;
id prevObject;
for (int i = toIndex; i <= fromIndex; i++){
prevObject = cObject;
cObject = [self objectAtIndex:i];
[self replaceObjectAtIndex:i withObject:(i == toIndex) ? movingObject : prevObject];
}
}
}
So, anybody want to chime in as to what's going on?

Label display not instant with iPhone app

I am developing an application for the iPhone. The question I have is how to display a new label with a different text every .5 seconds. For example, it would display Blue, Red, Green, Orange and Purple; one right after one another. Right now I am doing this:
results = aDictionary;
NSArray *myKeys = [results allKeys];
NSArray *sortedKeys = [myKey sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
int keyCount = [sortedKeys count];
while (flag == NO) {
NSTimeInterval timeMS = [startDate timeIntervalSinceNow] * -10000.0;
if (timeMS >= i) {
ii++;
i += 1000;
NSLog(#"endDate = %f", timeMS);
int randomNumber = rand() % keyCount + 1;
lblResult.text = [results valueForKey:[sortedKeys objectAtIndex:(randomNumber - 1)]];
result = [results valueForKey:[sortedKeys objectAtIndex:(randomNumber - 1)]];
lblResult.text = result;
}
if (ii > 25) {
flag = YES;
}
}
lblResult.text = [results valueForKey:[sortedKeys objectAtIndex:(sortedKeys.count - 1)]];
this function is called at the viewDidAppear Function and currently isn't displaying the new labels. It only displays the one at the end. Am I doing anything wrong? What would be the best method to approach this?
The problem is that you're not giving the run loop a chance to run (and therefore, drawing to happen). You'll want to use an NSTimer that fires periodically and sets the next text (you could remember in an instance variable where you currently are).
Or use something like this (assuming that items is an NSArray holding your strings):
- (void)updateText:(NSNumber *)num
{
NSUInteger index = [num unsignedInteger];
[label setText:[items objectAtIndex:index]];
index++;
// to loop, add
// if (index == [items count]) { index = 0; }
if (index < [items count]) {
[self performSelector:#selector(updateText:) withObject:[NSNumber numberWithUnsignedInteger:index] afterDelay:0.5];
}
}
At the beginning (e.g. in viewDidAppear:), you could then call
[self updateText:[NSNumber numberWithUnsignedInteger:0]];
to trigger the initial update.
You'd of course need to ensure that the performs are not continuing when your view disappears, you could do this by canceling the performSelector, or if you're using a timer, by simply invalidating it, or using a boolean, or ...
And if you want to get really fancy, use GCD :)

About the fate of malloc()ed arrays of arrays

my first question on Stackoverflow.
Let me start with a bit of code. It's a bit repetitive so I'm going to cut out the parts I repeat for different arrays (feel free to ask for the others). However, please ignore the code in preference to answering the Qs at the bottom. Firstly: thank you to answerers in advance. Secondly: the freeing of data.
#implementation ES1Renderer
GLfloat **helixVertices;
GLushort **helixIndices;
GLubyte **helixColors;
- (void)freeEverything
{
if (helixVertices != NULL)
{
for (int i=0; i < alphasToFree / 30 + 1; i++)
free(helixVertices[i]);
free(helixVertices);
}
if (helixIndices != NULL)
{
for (int i=0; i < alphasToFree / 30 + 1; i++)
free(helixIndices[i]);
free(helixIndices);
}
if (helixColors != NULL)
{
for (int i=0; i < alphasToFree / 30 + 1; i++)
free(helixColors[i]);
free(helixColors);
}
}
(I will get to the calling of this in a moment). Now for where I malloc() the arrays.
- (void)askForVertexInformation
{
int nrows = self.helper.numberOfAtoms / 300;
int mrows = [self.helper.bonds count] / 300;
int alphaCarbonRows = [self.helper.alphaCarbons count] / 30;
helixVertices = malloc(alphaCarbonRows * sizeof(GLfloat *) + 1);
helixIndices = malloc(alphaCarbonRows * sizeof(GLfloat *) + 1);
helixColors = malloc(alphaCarbonRows * sizeof(GLfloat *) + 1);
for (int i=0; i < alphaCarbonRows + 1; i++)
{
helixVertices[i] = malloc(sizeof(helixVertices) * HELIX_VERTEX_COUNT * 3 * 33);
helixIndices[i] = malloc(sizeof(helixIndices) * HELIX_INDEX_COUNT * 2 * 3 * 33);
helixColors[i] = malloc(sizeof(helixColors) * HELIX_VERTEX_COUNT * 4 * 33);
}
[self.helper recolourVerticesInAtomRange:NSMakeRange(0, [self.helper.alphaCarbons count]) withColouringType:CMolColouringTypeCartoonBlue forMasterColorArray:helixColors forNumberOfVertices:HELIX_VERTEX_COUNT difference:30];
self.atomsToFree = self.helper.numberOfAtoms;
self.bondsToFree = [self.helper.bonds count];
self.alphasToFree = [self.helper.alphaCarbons count];
}
Finally, the bit which calls everything (this is a separate class.)
- (void)loadPDB:(NSString *)pdbToLoad
{
if (!self.loading)
{
[self performSelectorOnMainThread:#selector(stopAnimation) withObject:nil waitUntilDone:YES];
[self.renderer freeEverything];
[renderer release];
ES1Renderer *newRenderer = [[ES1Renderer alloc] init];
renderer = [newRenderer retain];
[self performSelectorOnMainThread:#selector(stopAnimation) withObject:nil waitUntilDone:YES]; // need to stop the new renderer animating too!
[self.renderer setDelegate:self];
[self.renderer setupCamera];
self.renderer.pdb = nil;
[renderer resizeFromLayer:(CAEAGLLayer*)self.layer];
[newRenderer release];
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(setup:) object:pdbToLoad];
[self.queue addOperation:invocationOperation];
[invocationOperation release];
}
}
- (void)setup:(NSString *)pdbToLoad
{
self.loading = YES;
[helper release];
[renderer.helper release];
PDBHelper *aHelper = [[PDBHelper alloc] initWithContentsOfFile:pdbToLoad];
helper = [aHelper retain];
renderer.helper = [aHelper retain];
[aHelper release];
if (!resized)
{
[self.helper resizeVertices:11];
resized = YES;
}
self.renderer.helper = self.helper;
[self.helper setUpAtoms];
[self.helper setUpBonds];
if (self.helper.numberOfAtoms > 0)
[self.renderer askForVertexInformation];
else
{
// LOG ME PLEASE.
}
[self performSelectorOnMainThread:#selector(removeProgressBar) withObject:nil waitUntilDone:YES];
[self performSelectorOnMainThread:#selector(startAnimation) withObject:nil waitUntilDone:YES];
self.renderer.pdb = pdbToLoad;
self.loading = NO;
}
What I'm doing here is loading a molecule from a PDB file into memory and displaying it on an OpenGL view window. The second time I load a molecule (which will run loadPDB: above) I get the Giant Triangle Syndrome and Related Effects... I will see large triangles over my molecule.
However, I am releasing and reallocating my PDBHelper and ES1Renderer every time I load a new molecule. Hence I was wondering:
1. whether the helixVertices, helixIndices and helixColors which I have declared as class-wide variables are actually re-used in this instance. Do they point to the same objects?
2. Should I be setting all my variables to NULL after freeing? I plan to do this anyway, to pick up any bugs by getting a segfault, but haven't got round to incorporating it.
3. Am I even right to malloc() a class variable? Is there a better way of achieving this? I have no other known way of giving this information to the renderer otherwise.
I can't answer your general questions. There's too much stuff in there. However, this caught my eye:
[helper release];
[renderer.helper release];
PDBHelper *aHelper = [[PDBHelper alloc] initWithContentsOfFile:pdbToLoad];
helper = [aHelper retain];
renderer.helper = [aHelper retain];
[aHelper release];
I think this stuff possibly leaks. It doesn't make sense anyway.
If renderer.helper is a retain or copy property, do not release it. It already has code that releases old values when it is assigned new values. Also do not retain objects you assign to it.
You have alloc'd aHelper, so there's no need to retain it again. The above code should be rewritten something like:
[helper release];
helper = [[PDBHelper alloc] initWithContentsOfFile:pdbToLoad];
renderer.helper = helper;
Also, I think your helix malloced arrays should probably be instance variables. As things stand, if you have more than one ES1Renderer, they are sharing those variables.