Search code examples
arraysobjective-cmemorynsevent

Bad access in certain Obj-C Functions


So I am working on a sort of GLFW-lite framework and am doing the cocoa part. I am trying to get the input working but it is not working. Basically, my view interface has a pointer to a struct with an array in it. I know for a fact that the array is initialized because in it's initialization function I print out some values from it. (Also, I initialized the array) But in the keyDown function, it gives me an exc_bad_access for some reason. I did my best to replicate the error in the least lines of code possible while still keeping the same program structure. (Also, don't give me a hard time about the structure of the replication, the library is designed in a cleaner way.)

#include <Cocoa/Cocoa.h>
#include <stdlib.h>

typedef struct _Window {
    //For storing key states
    int *keys;
} Window;

int numKeys = 5;

Window* createWindow() {
    Window* window = malloc(sizeof(Window));

    window->keys = malloc(sizeof(int) * numKeys);
    memset(window->keys, 0, numKeys);

    return window;
}

@interface View : NSView {
    Window* win;
}
- (id) init:(Window*)window;
@end

@implementation View
- (id) init:(Window*)window {
    win = window;
    self = [[View alloc] initWithFrame: NSMakeRect(0, 0, 800, 600)];
    return self;
}

- (BOOL)acceptsFirstResponder {
    return YES;
}

- (void) keyDown:(NSEvent *)event {
    win->keys[4] = 1;
}
@end

int main() {
    [NSApplication sharedApplication];
    NSWindow *window = [[NSWindow alloc] initWithContentRect: NSMakeRect(0, 0, 800, 600)
                                                   styleMask: NSWindowStyleMaskClosable
                                                            | NSWindowStyleMaskTitled
                                                            | NSWindowStyleMaskResizable
                                                            | NSWindowStyleMaskMiniaturizable
                                                     backing: NSBackingStoreBuffered
                                                       defer: NO];
    [window center];
    [window setTitle:@"Window Title"];
    [window makeKeyAndOrderFront:nil];
    [window orderFrontRegardless];

    Window* keyStates = createWindow();

    View* view = [[View alloc] init:keyStates];

    [window setContentView:view];
    [window makeFirstResponder:view];

    [NSApp run];
}

If I try to access the window keys in the initialization function of the interface it works. So why does it give me the error?


Solution

  • Here's what happens:

    In main():

    View* view = [[View alloc] init:keyStates];
    

    View A is allocated and init: is called.

    In init:

    win = window;
    

    View A's win is window.

    self = [[View alloc] initWithFrame: NSMakeRect(0, 0, 800, 600)];
    

    View B is allocated and initWithFrame: is called.

    return self;
    

    View B is returned. View B doesn't have a win.

    Solution: don't allocate another View in init:.

    - (instancetype)init:(Window *)window {
        if (self = [super initWithFrame:NSMakeRect(0, 0, 800, 600)] {
            win = window;
        }
        return self;
    }