I was wondering if it's possible to pass the reference of an NSWindow to a custom object and then use that object to to add an NSButton and associated action/selector for that button.
I seem to be running into issues when I attempt this. When I run the sample program and click on the button, the following runtime error occurs: Thread 1: EXC_BAD_ACCESS (code=1, address=0x18)
Here is my code:
// AppDelegate.h
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
@property (assign) IBOutlet NSWindow *window;
@end
// AppDelegate.m
#import "AppDelegate.h"
#import "CustomObject.h"
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
CustomObject *test = [[CustomObject alloc]init];
[test createButton:_window];
}
@end
// CustomObject.h
#import <Foundation/Foundation.h>
@interface CustomObject : NSObject
{
int test;
NSButton *testButton;
}
- (IBAction)pressCustomButton:(id)sender;
-(void)createButton:(NSWindow*)win;
@end
// CustomObject.m
#import "CustomObject.h"
@implementation CustomObject
-(IBAction)pressCustomButton:(id)sender
{
NSLog(@"pressCustomButton");
}
-(void)createButton:(NSWindow*)win
{
testButton = [[NSButton alloc] initWithFrame:NSMakeRect(100, 100, 200, 50)];
[[win contentView] addSubview: testButton];
[testButton setTitle: @"Button title!"];
[testButton setButtonType:NSMomentaryLightButton]; //Set what type button You want
[testButton setBezelStyle:NSRoundedBezelStyle]; //Set what style You want
[testButton setTarget:self];
[testButton setAction:@selector(pressCustomButton:)];
}
@end
First off, I assume you're using Automatic Reference Counting.
When you click the button, the app attempts to call the pressCustomButton:
method of the target of the button, which the instance of CustomObject
set to be itself. However, that instance of CustomObject
has already been deallocated.
Take the following code:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
CustomObject *test = [[CustomObject alloc]init];
[test createButton:_window];
}
Once that method is finished being called, if you are using ARC, the CustomObject
instance you created will be released. Since NSControl
subclasses like NSButton
don't retain their targets (to avoid retain cycles/strong reference cycles), this will also cause the CustomObject
instance to be deallocated. This will cause any subsequent messages to that instance to produce unexpected results such as a crash.
To prevent this, you'll need to keep the CustomObject
instance around beyond the applicationDidFinishLaunching:
method. There are a couple of ways of doing that, such as making it a property of the AppDelegate
, or if you plan on having multiple objects, use an NSMutableArray
to store them in.
Something like the following:
@interface AppDelegate : NSObject <NSApplicationDelegate>
....
@property (nonatomic, strong) NSMutableArray *customObjects;
@end
// AppDelegate.m
#import "AppDelegate.h"
#import "CustomObject.h"
@implementation AppDelegate
- (id)init {
if ((self = [super init])) {
customObjects = [[NSMutableArray alloc] init];
}
return self;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
CustomObject *test = [[CustomObject alloc]init];
[customObjects addObject:test];
[test createButton:_window];
}
@end