Custom NSView forgets fields?

Discussion in 'Mac Programming' started by ryanknu, Oct 26, 2008.

  1. ryanknu macrumors newbie

    Joined:
    Sep 12, 2008
    #1
    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?
     
  2. HiRez macrumors 603

    HiRez

    Joined:
    Jan 6, 2004
    Location:
    Western US
    #2
    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).
     
  3. ryanknu thread starter macrumors newbie

    Joined:
    Sep 12, 2008
    #3
    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.
     
  4. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #4
    Looks like a memory management issue. How are you assigning field?
     
  5. ryanknu thread starter macrumors newbie

    Joined:
    Sep 12, 2008
    #5
    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
     
  6. SydneyDev macrumors 6502

    Joined:
    Sep 15, 2008
    #6
    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
     
  7. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #7
    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.
     

Share This Page