Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

ryanknu

macrumors newbie
Original poster
Sep 12, 2008
12
0
Hey everyone,
I have this problem where I'm making a simple minesweeper game. For some reason, at the end of the NSView constructor the object's fields are all initialized and set:
http://ryanknu.com/drop/p1.png

But for some reason, when the drawRect method is called the object no longer has any data in it:
http://ryanknu.com/drop/p2.png

Has anyone seen this before? Or what can I do to fix that?
 
Were those two screenshots taken in the same run of the program? Because I notice that "self" has a different address at each breakpoint, so it is not the same object. I can't really tell what is wrong but my first suspicion is that you are perhaps initializing this object in two different places, maybe once in the nib and once again in code, or in two different places in the nib(s), or something. How are your nibs set up and are you creating one of those objects in code anywhere? If it's in the nib you don't need to (and shouldn't) instantiate it in code because it's already a live object (well, after all the nibs are loaded and awakened).
 
I only have the view dragged into the nib, no instances of the object are created in the code at all. On running it stopped at that breakpoint, I took the screenshot, then hit continue, then it stopped at bad access. I am really stumped by this.
 
The constructor calls makeMineField and initialize in the minecore.c file. Then, reason unbeknown to me, later in the draw method I have no idea where all that data went.

Code:
strutures:

typedef struct {
	int row;
	int col;
} gridLocation;

typedef struct {
	int *field;
	int height;
	int width;
} mineField;

Code:
mineview.m

#import "minecore.h"
#import "mineview.h"
 

@implementation mineview

- (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
		dead = NO;
        //r = 20;
		//c = 20;
        field = nil;
		mines = 40;
        field = makeMineField(20, 20);
		initialize(field, mines);
    }
    return self;
}

-(void) action:(int) action x:(int)x y:(int)y {
	// actions: 1 reveal, 2 flag, 3 reveal around
	if ( dead ) return;
	if ( x >= (*field).width || y >= (*field).height )
		return;
    gridLocation loc;
    loc.row = x;
    loc.col = y;    
	if (action == 1) {
		int ret = uncover(field, loc);
		if ( ret == 0 )
			dead = YES; // hit a mine :? todo
	} else if (action == 2) {
		mark(field, loc);
	}
	
	[self setNeedsDisplay: YES];
}

-(void) mouseDown:(NSEvent *) theEvent {
	NSPoint pt = [theEvent locationInWindow];
	int x = pt.y / (boxsize + 1);
	int y = pt.x / (boxsize + 1);
	[self action: 1 x:x y:y];
}

-(void) rightMouseDown:(NSEvent *) theEvent {
	NSPoint pt = [theEvent locationInWindow];
	int x = pt.y / (boxsize + 1);
	int y = pt.x / (boxsize + 1);
	[self action: 2 x:x y:y];
}

- (void)drawRect:(NSRect)rect {
    [[NSColor whiteColor] set];
	[NSBezierPath fillRect: rect];
	
	float w = rect.size.width;
	float h = rect.size.height;
	
	float maxsizeboxx = (w - (*field).width) / (*field).width;
	float maxsizeboxy = (h - (*field).height) / (*field).height;
	
	float size = maxsizeboxx;
	if ( maxsizeboxy < maxsizeboxx )
		size = maxsizeboxy;
	boxsize = size;
	
	if (boxsize > 100 && boxsize < 1000)
		boxsize ++;
	
	int i, j;
	
	if ( dead ) {
		[[NSColor magentaColor] set];
		[@"oops, you died. restart the application to restart, I guess" drawAtPoint: NSMakePoint(0,0) withAttributes:nil];
		return;
	}
    
    gridLocation loc;
	for (i = 0; i < (*field).height; i++) {
		for (j = 0; j < (*field).width; j++) {
			[[NSColor grayColor] set];
            loc.row = i;
            loc.col = j;
			if ( isMarked(field, loc) )
				[[NSColor redColor] set];
			if ( isUncovered(field, loc) ) 
				[[NSColor whiteColor] set];
			[NSBezierPath fillRect: NSMakeRect(j + (size * j), i + (size * i), size, size)];
			if ( isUncovered(field, loc) ) {
				int n = hazardsAround(field, loc);
				if ( n > 0 ) {
					[[NSColor blackColor] set];
					NSPoint np = {j + (size * j) + (.33 * size), i + (size * i) + (.33 * size)};
					NSString * s = [[NSNumber numberWithInt: n] stringValue];
					[s drawAtPoint: np withAttributes:nil];
				}
			}
		}
	}
}

Code:
minecore.c

#include <stdlib.h>
#include "minecore.h"

int randIInt(int min, int max) {
	static int sr = 0;
	if ( ! sr )
		srandomdev();
	sr = 1;
	return (int) ( (double) ( (double) random() / (double) RAND_MAX ) * max + min );
}

// mine constants:
//  1: mine, covered,   unflagged
//  2: mine, covered,   flagged
//  3: free, covered,   unflagged
//  4: free, uncovered, n/a
//  5: free, covered,   flagged
//  6: 

mineField *makeMineField(int rows, int cols) {
	mineField *ret = malloc(sizeof(mineField));
	(*ret).field  = (int *) malloc(sizeof(int) * rows * cols);
	(*ret).height = rows;
	(*ret).width  = cols;
	return ret;
}

void resizeMineField(mineField *mineData, int newRows, int newCols) {
	realloc( (*mineData).field, sizeof(int) * newRows * newCols);
	if ( (*mineData).field == NULL )
		exit(0);
	(*mineData).height = newRows;
	(*mineData).width  = newCols;
}

void destroyMineField(mineField *mineData) {
	free( (*mineData).field );
}

int isOutOfBounds(mineField *mineData, gridLocation location) {
	return (    location.row < 0
			 || location.col < 0
			 || location.row >= (*mineData).height
			 || location.col >= (*mineData).width );
}

int codeAtLocation(mineField *mineData, gridLocation location) {
	if ( isOutOfBounds(mineData, location) ) {
		free( (*mineData).field );
		exit(0);
	}
	return *( (*mineData).field + ( location.row * (*mineData).width ) + location.col );
}

int *referenceAtLocation(mineField *mineData, gridLocation location) {
    return (*mineData).field + ( location.row * (*mineData).width ) + location.col;
}

... omitted

void initialize(mineField *mineData, int mines) {
    int i, j;
    for ( i = 0; i < (*mineData).width; i++ ) {
        for ( j = 0; j < (*mineData).height; j++ ) {
            gridLocation loc;
            loc.row = i;
            loc.col = j;
            int * rol = referenceAtLocation( mineData, loc );
            *rol = 3;
        }
    }
    for ( i = 0; i < mines; i++ ) {
        gridLocation tempLoc;
        tempLoc.row = randIInt(0, (*mineData).width);
        tempLoc.col = randIInt(0, (*mineData).height);
        while ( isHazardous( mineData, tempLoc) ) {
            tempLoc.row = randIInt(0, (*mineData).width);
            tempLoc.col = randIInt(0, (*mineData).height);
        }
        int * rol = referenceAtLocation( mineData, tempLoc );
        *rol = 1;
    }
}

... omitted
 
I think the Nib loading process inits your object and then overwrites any values with whatever was set in Interface Builder.

Any manual initialization you want to do should be in - (void)awakeFromNib
 
I think the Nib loading process inits your object and then overwrites any values with whatever was set in Interface Builder.

Any manual initialization you want to do should be in - (void)awakeFromNib

This is only true for IBOutlets. Anything else is fine.


I think what HiRez says might be right. Are you sure you don't have a 2nd view object being initialized in your nib, outside of a window or view? You could override the init method in your subclass and see if that's getting called instead of initWithFrame, because if that's so, then that might explain why your data is null.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.