Removing object from NSMutableArray after upgrading to SDK 5 for iOS causes my app to crash - objective-c

[Edit] I rearranged how I remove obj from the list. That's originally how it was still causing the same error.
This is the code in question. I believe I have the error narrowed down to the line where it removes the object from the _blocks NSMutableArray. I have this exact same code in other parts of my code removing similar object from the same array. For some reason when this function is called it causes the game to crash. It didn't have this problem before I upgraded to the latest XCode which supports iOS SDK 5 and armv7. Before I upgraded XCode it worked fine. I would walk into an item and the item would disappear from the screen. Now it just crashes when I get an item. Any help would be greatly appreciated.
-(void)itemCollision:(Collidable *)obj :(int)itemID :(int)objID: (bool)withPlayer{
[background removeChild:[obj getSprite] cleanup:YES];
[background removeChild:[obj getArrow] cleanup:YES];
[_blocks removeObject:obj];
//[obj release];
if(withPlayer){
if(itemID == 1){
[[SimpleAudioEngine sharedEngine] playEffect:#"Item2.mp3" pitch:1.0f pan:0.0f gain:0.3f];
points += (10 + level * 22);
}
}
}
#import <UIKit/UIKit.h>
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, #"AppDelegate");
[pool release];
return retVal;
}
When the app crashes it highlights this line:
int retVal = UIApplicationMain(argc, argv, nil, #"AppDelegate");
The error message is: "Thread 1: Program received signal: "SIGARBRT".
Here is the code where I call itemCollision:
-(bool) isCollision {
CCSprite *playerSprite = mob;//[mob getSprite];
CGRect playerRect = CGRectMake(playerSprite.position.x - (playerSprite.contentSize.width/2),
playerSprite.position.y - (playerSprite.contentSize.height/2),
playerSprite.contentSize.width,
playerSprite.contentSize.height);
//Player Collision
BOOL collision = FALSE;
for (Block *block in _blocks) {
CCSprite *blockSprite = [block getSprite];
CGRect blockRect = CGRectMake(blockSprite.position.x - (blockSprite.contentSize.width/2),
blockSprite.position.y - (blockSprite.contentSize.height/2),
blockSprite.contentSize.width,
blockSprite.contentSize.height + 1 + [mob getSpeed]);
if (CGRectIntersectsRect(blockRect, playerRect)) {
if ([block getItemID] != 0) {
[self itemCollision:block :[block getItemID] :[block getID] :TRUE];
continue;
}
if(playerSprite.position.y > blockSprite.position.y){
collision = TRUE;
}
if(collision) {
[[SimpleAudioEngine sharedEngine] playEffect:#"squish.caf"];
[self die];
return TRUE;
}
}
}
return FALSE;
}
Here is how I remove objects from _blocks elsewhere in the code. This is in the gravity timer function:
NSMutableArray *blocksToDelete = [[NSMutableArray alloc] init];
for (Block *block in _blocks) {
CCSprite *blockSprite = [block getSprite];
//[block addSpeed:gravity];
[block setPreX:blockSprite.position.x];
[block setPreY:blockSprite.position.y];
blockSprite.position = ccp(blockSprite.position.x, blockSprite.position.y - [block getSpeed]);
if (blockSprite.position.y < -30 + blockSprite.contentSize.height / 2) {
[blocksToDelete addObject:block];
}
if(blockSprite.position.y - blockSprite.contentSize.height < [block getArrow].position.y){
[block getArrow].visible = FALSE;
}
}
for (Block *b in blocksToDelete) {
[_blocks removeObject:b];
[background removeChild:[b getSprite] cleanup:YES];
[background removeChild:[b getArrow] cleanup:YES];
[b release];
}
I get to the items before they reach the point where they get deleted by this function. Again, this code has been working for months until I upgraded my XCode.

If the array had the last retain on the object, it will be dealloced when you remove it. You go on to use the object for the rest of the method, so it needs to stay alive. Certainly checking the retainCount of obj to see if the NSMutableArray has the last retain is the first place to start.
If we assume, for the moment, that that's the case, then try this alternate approach:
-(void)itemCollision:(Collidable *)obj :(int)itemID :(int)objID: (bool)withPlayer
{
[[obj retain] autorelease];
[_blocks removeObject:obj];
[background removeChild:[obj getSprite] cleanup:YES];
[background removeChild:[obj getArrow] cleanup:YES];
if(withPlayer){
if(itemID == 1){
[[SimpleAudioEngine sharedEngine] playEffect:#"Item2.mp3" pitch:1.0f pan:0.0f gain:0.3f];
points += (10 + level * 22);
}
}
}
When the debugger goes to main.m, it's often because it's dying while popping an autorelease pool -- a task you don't have debug symbols for, so it goes to the deepest stack frame for which it has source to show you, which in the case of autorelease pools on the main thread, will be your main() function.
You should also try running this case with the Zombies instrument in Instruments. It can be very helpful for tracking this stuff down.

It's hard to know exactly what's happening beyond the scope of the code sample you've shared, but a wild guess would be that your 'obj' is being over-released. It's released once when you remove it from the array, then you're releasing it again. This may or may not be appropriate considering the retain count of that object as its passed in.
To begin troubleshooting, try using the Instrument Zombie tool - that'll help you observe if it is indeed being overreleased.
You could additionally comment out your:
[obj release]
To see if that stops the error (which may or may not be the solution but should be insightful in any case).

Related

How to use a run loop with NSTimer in Objective-C

I am writing a small program for printing to the console every few seconds.
The objective is to call a function on each of ten objects in an array every N seconds, where n is a class variable. I was wondering how I could incorporate such a timer loop into my code. Here is what I have. I would ver much appreciate a response. Thanks.
ALHuman.m
static NSInteger barkInterval = 3;
#implementation ALHuman
-(void)setMyDog:(ALDog *)dog{
myDog = dog;
}
-(ALDog *)getMyDog{
return myDog;
}
+(NSInteger)returnBarkInterval {
return barkInterval;
}
-(void)createDog{
ALDog *aDog = [[ALDog alloc]init];
char dogName [40] = " ";
NSLog(#"Please enter a name for %s's dog",[self name]);
scanf("%s",dogName);
[aDog setName:dogName];
char barkSound [40] = "";
NSLog(#"Please enter a bark sound for dog: %s",[aDog name]);
scanf("%s",barkSound);
[myDog setBarkSound:barkSound];
[myDog setCanBark:YES];
[self setMyDog:aDog];
}
-(void)callDog:(NSInteger)numberOfResponses {
NSLog(#"%s",[[self getMyDog] name]);
[[self getMyDog] bark:numberOfResponses];
}
-(NSInteger)getRandomNumberBetween:(NSInteger)from to:(NSInteger)to {
return (NSInteger)from + arc4random() % (to-from+1);
}
-(void)timerFireMethod:(NSTimer *)timer {
[self callDog:[self getRandomNumberBetween:1 to:5]];
}
#end
main.m
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSMutableArray *people = [[NSMutableArray alloc]init];
for (int i = 0; i < 10; i++) { // I didn't want to create a class method for creating these humans and their dogs, because it is unneccessary.
ALHuman *person = [[ALHuman alloc]init];
// NameGenerator *name = [[NameGenerator alloc]init]; I need to work on implementing this
[person setName:"Bob"];
[person createDog];
[person setHeight:[person getRandomNumberBetween:5 to:8]];
[people addObject:person];
}
NSLog(#"%#",people);
NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop]; //Here is where I am having trouble
for(ALHuman *human in people){
[NSTimer timerWithTimeInterval:[ALHuman returnBarkInterval] target:human selector:#selector(timerFireMethod:) userInfo:NULL repeats:YES];
}
return 0;
}
}
First, +[NSTimer timerWithTimeInterval:...] creates and returns a new timer object, but you're ignoring the return value. So, those timer objects are just lost and useless. You either want to manually schedule them into the run loop or use +scheduledTimerWithTimeInterval:... instead.
Second, you allow execution to flow to the return statement, which exits the main() function. When that happens, the process is terminated. It doesn't bother waiting for any timers to fire, or anything else for that matter.
If you want to wait and allow those timers to fire, you need to manually run the run loop (since you're not in the main thread of an application, which would run the run loop for you). You can invoke [myRunLoop run], [myRunLoop runUntilDate:someDate], or build a loop around an invocation of [myRunLoop runMode:someMode beforeDate:someDate]. It depends on under what circumstances you want the program to exit, if ever.
Use this simple runloop controller class.
(untested):
int main(int argc, const char * argv[])
{
#autoreleasepool {
RunLoopController *runLoopController = [RunLoopController new];
[runLoopController register];
NSMutableArray *people = [NSMutableArray new];
NSMutableArray *timers = [NSMutableArray new];
for (int i = 0; i < 10; i++) {
ALHuman *person = [[ALHuman alloc]init];
// NameGenerator *name = [[NameGenerator alloc]init]; I need to work on implementing this
[person setName:"Bob"];
[person createDog];
[person setHeight:[person getRandomNumberBetween:5 to:8]];
[people addObject:person];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:[ALHuman returnBarkInterval]
target:person
selector:#selector(timerFireMethod:)
userInfo:nil
repeats:YES];
[timers addObject:timer];
}
NSLog(#"%#",people);
while ([runLoopController run])
;
[runLoopController deregister];
}
}
However the issue you face is how to terminate the program when you are finished. You can either install a signal handler, or use some other metric to determine the program has finished, and then call [[RunLoopController mainRunLoopController] terminate].
The RunLoopController uses a simple signalling mechanism (a MACH port) in order to know that the runloop must terminate. Other usage examples exist on the github repo in the above link.

Updating NSView asynchronously from a thread

First of all, I'm an Objective-C novice. So I'm not very familiar with OS X or iOS development. My experience is mostly in Java.
I'm creating an agent-based modeling-framework. I'd like to display the simulations and to do that I'm writing a little application. First, a little bit about the framework. The framework has a World class, in which there is a start method, which iterates over all agents and has them perform their tasks. At the end of one "step" of the world (i.e., after all the agents have done their thing), the start method calls the intercept method of an object that implements InterceptorProtocol. This object was previously passed in via the constructor. Using the interceptor, anyone can get a hook into the state of the world. This is useful for logging, or in the scenario that I'm trying to accomplish: displaying the information in a graphical manner. The call to intercept is synchronous.
Now as far as the GUI app is concerned, it is pretty simple. I have a controller that initializes a custom view. This custom view also implements InterceptorProtocol so that it can listen in, to what happens in the world. I create a World object and pass in the view as an interceptor. The view maintains a reference to the world through a private property and so once I have initialized the world, I set the view's world property to the world I have just created (I realize that this creates a cycle, but I need a reference to the world in the drawRect method of the view and the only way I can have it is if I maintain a reference to it from the class).
Since the world's start method is synchronous, I don't start the world up immediately. In the drawRect method I check to see if the world is running. If it is not, I start it up in a background thread. If it is, I examine the world and display all the graphics that I need to.
In the intercept method (which gets called from start running on the background thread), I set setNeedsToDisplay to YES. Since the start method of the world is running in a separate thread, I also have a lock object that I use to synchronize so that I'm not working on the World object while it's being mutated (this part is kind of janky and it's probably not working the way I expect it to - there are more than a few rough spots and I'm simply trying to get a little bit working; I plan to clean up later).
My problem is that the view renders some stuff, and then it pretty much locks up. I can see that the NSLog statements are being called and so the code is running, but nothing is getting updated on the view.
Here's some of the pertinent code:
MasterViewController
#import "MasterViewController.h"
#import "World.h"
#import "InfectableBug.h"
#interface MasterViewController ()
#end
#implementation MasterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_worldView = [[WorldView alloc] init];
World* world = [[World alloc] initWithName: #"Bhumi"
rows: 100
columns: 100
iterations: 2000
snapshotInterval: 1
interceptor: _worldView];
for(int i = 0; i < 999; i++) {
NSMutableString* name = [NSMutableString stringWithString: #"HealthyBug"];
[name appendString: [[NSNumber numberWithInt: i] stringValue]];
[world addBug: [[InfectableBug alloc] initWithWorld: world
name: name
layer: #"FirstLayer"
infected: NO
infectionRadius: 1
incubationPeriod: 10
infectionStartIteration: 0]];
}
NSLog(#"Added all bugs. Going to add infected");
[world addBug: [[InfectableBug alloc] initWithWorld: world
name: #"InfectedBug"
layer: #"FirstLayer"
infected: YES
infectionRadius: 1
incubationPeriod: 10
infectionStartIteration: 0]];
[_worldView setWorld: world];
//[world start];
}
return self;
}
- (NSView*) view {
return self.worldView;
}
#end
WorldView
#import "WorldView.h"
#import "World.h"
#import "InfectableBug.h"
#implementation WorldView
#synthesize world;
- (id) initWithFrame:(NSRect) frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void) drawRect:(NSRect) dirtyRect {
CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];
CGContextClearRect(myContext, CGRectMake(0, 0, 1024, 768));
NSUInteger rows = [world rows];
NSUInteger columns = [world columns];
NSUInteger cellWidth = 1024 / columns;
NSUInteger cellHeight = 768 / rows;
if([world running]) {
#synchronized (_lock) {
//Ideally we would need layers, but for now let's just get this to display
NSArray* bugs = [world bugs];
NSEnumerator* enumerator = [bugs objectEnumerator];
InfectableBug* bug;
while ((bug = [enumerator nextObject])) {
if([bug infected] == YES) {
CGContextSetRGBFillColor(myContext, 128, 0, 0, 1);
} else {
CGContextSetRGBFillColor(myContext, 0, 0, 128, 1);
}
NSLog(#"Drawing bug %# at %lu, %lu with width %lu and height %lu", [bug name], [bug x] * cellWidth, [bug y] * cellHeight, cellWidth, cellHeight);
CGContextFillRect(myContext, CGRectMake([bug x] * cellWidth, [bug y] * cellHeight, cellWidth, cellHeight));
}
}
} else {
[world performSelectorInBackground: #selector(start) withObject: nil];
}
}
- (BOOL) isFlipped {
return YES;
}
- (void) intercept: (World *) aWorld {
struct timespec time;
time.tv_sec = 0;
time.tv_nsec = 500000000L;
//nanosleep(&time, NULL);
#synchronized (_lock) {
[self setNeedsDisplay: YES];
}
}
#end
start method in World.m:
- (void) start {
running = YES;
while(currentIteration < iterations) {
#autoreleasepool {
[bugs shuffle];
NSEnumerator* bugEnumerator = [bugs objectEnumerator];
Bug* bug;
while((bug = [bugEnumerator nextObject])) {
NSString* originalLayer = [bug layer];
NSUInteger originalX = [bug x];
NSUInteger originalY = [bug y];
//NSLog(#"Bug %# is going to act and location %i:%i is %#", [bug name], [bug x], [bug y], [self isOccupied: [bug layer] x: [bug x] y: [bug y]] ? #"occupied" : #"not occupied");
[bug act];
//NSLog(#"Bug has acted");
if(![originalLayer isEqualToString: [bug layer]] || originalX != [bug x] || originalY != [bug y]) {
//NSLog(#"Bug has moved");
[self moveBugFrom: originalLayer atX: originalX atY: originalY toLayer: [bug layer] atX: [bug x] atY: [bug y]];
//NSLog(#"Updated bug position");
}
}
if(currentIteration % snapshotInterval == 0) {
[interceptor intercept: self];
}
currentIteration++;
}
}
//NSLog(#"Done.");
}
Please let me know if you'd like to see any other code. I realize that the code is not pretty; I was just trying to get stuff to work and I plan on cleaning it up later. Also, if I'm violating an Objective-C best practices, please let me know!
Stepping out for a bit; sorry if I don't respond immediately!
Whew, quiet a question for probably a simple answer: ;)
UI updates have to be performed on the main thread
If I read your code correctly, you call the start method on a background thread. The start method contains stuff like moveBugFrom:... and also the intercept: method. The intercept method thus calls setNeedsDisplay: on a background thread.
Have all UI related stuff perform on the main thread. Your best bet is to use Grand Central Dispatch, unless you need to support iOS < 4 or OS X < 10.6 (or was it 10.7?), like this:
dispatch_async(dispatch_get_main_queue(), ^{
// perform UI updates
});

Recursive Blocks in Objective-C leaking in ARC

So I'm using recursive blocks. I understand that for a block to be recursive it needs to be preceded by the __block keyword, and it must be copied so it can be put on the heap. However, when I do this, it is showing up as a leak in Instruments. Does anybody know why or how I can get around it?
Please note in the code below I've got references to a lot of other blocks, but none of them are recursive.
__block NSDecimalNumber *(^ProcessElementStack)(LinkedList *, NSString *) = [^NSDecimalNumber *(LinkedList *cformula, NSString *function){
LinkedList *list = [[LinkedList alloc] init];
NSDictionary *dict;
FormulaType type;
while (cformula.count > 0) {
dict = cformula.pop;
type = [[dict objectForKey:#"type"] intValue];
if (type == formulaOperandOpenParen || type == formulaListOperand || type == formulaOpenParen) [list add:ProcessElementStack(cformula, [dict objectForKey:#"name"])];
else if (type == formulaField || type == formulaConstant) [list add:NumberForDict(dict)];
else if (type == formulaOperand) [list add:[dict objectForKey:#"name"]];
else if (type == formulaCloseParen) {
if (function){
if ([function isEqualToString:#"AVG("]) return Average(list);
if ([function isEqualToString:#"MIN("]) return Minimum(list);
if ([function isEqualToString:#"MAX("]) return Maximum(list);
if ([function isEqualToString:#"SQRT("]) return SquareRoot(list);
if ([function isEqualToString:#"ABS("]) return EvaluateStack(list).absoluteValue;
return EvaluateStack(list);
} else break;
}
}
return EvaluateStack(list);
} copy];
NSDecimalNumber *number = ProcessElementStack([formula copy], nil);
UPDATE
So in my own research I've discovered that the problem apparently does have to do with the references to the other blocks this block uses. If I do something simple like this, it doesn't leak:
__block void (^LeakingBlock)(int) = [^(int i){
i++;
if (i < 100) LeakingBlock(i);
} copy];
LeakingBlock(1);
However, if I add a another block in this, it does leak:
void (^Log)(int) = ^(int i){
NSLog(#"log sub %i", i);
};
__block void (^LeakingBlock)(int) = [^(int i){
Log(i);
i++;
if (i < 100) LeakingBlock(i);
} copy];
LeakingBlock(1);
I've tried using the __block keyword for Log() and also tried copying it, but it still leaks. Any ideas?
UPDATE 2
I found a way to prevent the leak, but it's a bit onerous. If I convert the passed in block to a weak id, and then cast the weak id back into a the block type, I can prevent the leak.
void (^Log)(int) = ^(int i){
NSLog(#"log sub %i", i);
};
__weak id WeakLogID = Log;
__block void (^LeakingBlock)(int) = [^(int i){
void (^WeakLog)(int) = WeakLogID;
WeakLog(i);
if (i < 100) LeakingBlock(++i);
} copy];
LeakingBlock(1);
Surely there's a better way?
Ok, I found the answer on my own...but thanks to those who tried to help.
If you're referencing/using other blocks in a recursive block, you must pass them in as weak variables. Of course, __weak only applies to block pointer types, so you must typedef them first. Here's the final solution:
typedef void (^IntBlock)(int);
IntBlock __weak Log = ^(int i){
NSLog(#"log sub %i", i);
};
__block void (^LeakingBlock)(int) = ^(int i){
Log(i);
if (i < 100) LeakingBlock(++i);
};
LeakingBlock(1);
The above code doesn't leak.
Aaron,
As your code appears to be single threaded, why are you copying the block? If you don't copy the block, you don't have a leak.
Andrew
Without further context information, I can say this:
You are leaking that block because you are copying it and not releasing it elsewhere. You need to copy it to move it to the heap, that's ok. But the way you've chosen is not entirely ok.
A correct way to do it is to store it as some object instance variable, copy it, and then release it inside dealloc. At least, that's a way to do it without leaking.

App crashes on drawing of UILabel - NSString is a zombie

I'm working on an iPad app that has a slider that is used to scroll through data. When scrolling, a map is displayed and the data is updated. The problem is, if you scroll fast enough (or somehow trigger the race condition), the app crashes on accessing a zombie NSString. I've been able to track it down in the Profiler and found this:
Event Type RefCt Timestamp Size Responsible Library Responsible Caller
Malloc 1 01:55.166.466 16 Foundation -[NSPlaceholderString initWithFormat:locale:arguments:]
Autorelease <null> 01:55.166.472 0 Foundation +[NSString stringWithFormat:]
CFRetain 2 01:55.166.473 0 My Program -[StateView updateVotes:]
CFRetain 3 01:55.166.476 0 UIKit -[UILabel setText:]
CFRelease 2 01:55.166.504 0 My Program -[StateView updateVotes:]
CFRelease 1 01:55.177.661 0 Foundation -[NSAutoreleasePool release]
CFRelease 0 01:55.439.090 0 UIKit -[UILabel setText:]
Zombie -1 01:55.439.109 0 UIKit -[NSString(UIStringDrawing) drawAtPoint:forWidth:withFont:lineBreakMode:letterSpacing:includeEmoji:]
I'm using ARC on iOS5, so I'm not in control of the retain/release at all. Even if I was, looking at the above, it is correct. The problem seems to be a race condition between the drawing function and the UILabel string actually changing. The UILabel releases the first string, as a new one has been set, but the drawing function is holding a reference to it somehow, but did not retain it.
As a note, I have not modified the UILabel in any way.
Any ideas?
--- Code added as update:
Slider update:
-(void)sliderValueChanged:(UISlider *)slider {
float position = slider.value - 1790.0f;
int year;
if(position <= 0.0f) {
year = 1789;
} else {
year = 1792 + (floor(position / 4.0f)*4);
}
[self setYear:year];
}
setYear:
-(void)setYear:(int)year {
if (year == currentYear) {
// year didn't change, so don't do anything
return;
}
[yearLabel setText:[[NSString alloc] initWithFormat:#"%i", year]];
currentYear = year;
[self getMapForYear:year];
}
getMapForYear:
-(void) getMapForYear:(int)year {
[self setToMap:[historicalData objectForKey:[NSNumber numberWithInt:year]];
}
setToMap:
-(void) setToMap:(HistoricalMap *)map {
// Label the map
for (State *state in [map states]) {
[mapController setVotes:[state votes] forState:[state abbreviation]];
}
}
setVotes:forState:
-(void)setVotes:(NSNumber *)votes forState:(NSString *)stateAbbreviation {
StateView *state = [states objectForKey:stateAbbreviation];
if (state == nil) {
NSLog(#"Invalid State Votes -- %#", stateAbbreviation);
return;
}
[state updateVotes:votes];
[state setNeedsDisplay];
}
updateVotes:
-(void)updateVotes:(NSNumber *)newVotes {
[self setVotes:newVotes];
NSString *voteString = [[NSString alloc] initWithFormat:#"%#", newVotes];
[voteLabel setText:voteString];
if ([newVotes isEqual:[NSNumber numberWithInt:0]]) {
[[self voteLabel] setHidden:YES];
[[self stateAbbreviationLabel] setHidden:YES];
} else {
[[self stateAbbreviationLabel] setHidden:NO];
[[self voteLabel] setHidden:NO];
}
}
I think you are attempting to do far too much during the slider's movement. Creating and executing core data fetch requests alone would seem to be overkill, let alone updating the entire GUI and a screenful of labels. Have you tested the performance of this on a device?
It could be worth profiling these sections of code and seeing where the time is spent. You could look at caching the fetch requests or the results, for example, or you may have to resort to updating only when the slider has stopped, or only for every n increments along the path.
You havw several memory-leaks with NSString:
[yearLabel setText:[[NSString alloc] initWithFormat:#"%i", year]]; // leak
Create string with stringWithFormat method instead
[yearLabel setText:[NSString stringWithFormat:#"%i", year]];
[NSString stringWithFormat: **is the best way formatting the string than any other..**

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.