0

I am trying to add an action to a button programmatically from within a custom class. However, I keep getting an error when I perform the action. I've read a lot about how to do this but am clearly making a mistake somewhere and can't figure out where.

The button is created in the first instance by dragging it onto the storyboard. I then control drag to the ViewControler.h file to get this:

@property (strong, nonatomic) IBOutlet UIButton *testButtonForClass;

In ViewControler.m, I do this:

- (void)viewDidLoad
{
    [super viewDidLoad];

    testClass *myClass = [[testClass alloc]init];
    myClass.myButton = self.testButtonForClass;
    [myClass assignActionTargets];
}

Below is the custom class Header and Implementation file.

Header File

#import <Foundation/Foundation.h> 

@interface testClass : NSObject

@property (nonatomic, strong) UIButton *myButton;

-(void)assignActionTargets;

@end

Implementation File

#import "testClass.h"

@implementation testClass

-(void)assignActionTargets{    
    [self.myButton addTarget:
    self action:@selector(myButtonInnerTap) 
    forControlEvents:(UIControlEventTouchUpInside)]; 
}

-(void)myButtonInnerTap{
     UIAlertView *a = [[UIAlertView alloc]initWithTitle:nil 
     message:@"testClass says hello" 
     delegate:nil 
     cancelButtonTitle:@"Dismiss" 
     otherButtonTitles:nil, nil];

    [a show];
}

@end
Cœur
  • 37,241
  • 25
  • 195
  • 267
Sparked
  • 844
  • 10
  • 25
  • 1
    what's the error you're getting? – Gabriele Petronella Feb 16 '14 at 13:54
  • I'm new to Objective-C and am not good at making use of the debugger. The programme compiles okay but when I click the button that I've assigned the class button to I get this: EXC_BAD_ACCESS(code=2, address=0x8), in Main.m – Sparked Feb 16 '14 at 13:57
  • Add the exception breakpoint and see if you get any better information. see this question for how to set this up http://stackoverflow.com/questions/17802662/exception-breakpoint-in-xcode – Paul.s Feb 16 '14 at 14:01
  • `otherButtonTitles:nil, nil];` also try to remove extra nil – Mykola Denysyuk Feb 16 '14 at 14:03
  • can you paste the code of your controller. how you are calling function on button tap. – Pawan Rai Feb 16 '14 at 14:04
  • Use the debugger, Luke. Specifically, look at the stack trace that is given to you when it crashes... set a break point at an early enough frame, and then step through the code from there to see where you are accessing invalid memory (most likely referencing a null pointer). – Jody Hagins Feb 16 '14 at 14:04
  • Thank you, everyone, I've added an exception breakpoint but it the error seems to be the same. I will update the code in the main question to describe how I assign the button in the ViewController class. – Sparked Feb 16 '14 at 14:15
  • @Sparked You probably forgot to retain your testClass instance in view controller. Put `- (void)dealloc { NSLog(@"dealloc"); }` in testClass implementation just to be sure. – user3125367 Feb 16 '14 at 14:17

2 Answers2

1

You create a testClass instance, but you don't keep a reference to, so it gets deallocated at the end of viewDidLoad.

- (void)viewDidLoad {
    [super viewDidLoad];

    TestClass *myClass = [[TestClass alloc] init];
    myClass.myButton = self.testButtonForClass;
    [myClass assignActionTargets];

    // myClass gets deallocated here!
}

When the button is clicked, it tries to access the target you specify, but now that's a dangling pointer to an invalid memory segment, hence leading to a crash.

You have to keep a strong reference to myClass in order to keep it alive by the time the button is clicked. Declaring a strong property is a good way of achieving that.

@property (nonatomic, strong) TestClass *myThingy;

- (void)viewDidLoad {
    [super viewDidLoad];

    self.myThingy = [[testClass alloc] init];
    self.myThingy.myButton = self.testButtonForClass;
    [self.myThingy assignActionTargets];
}

Code style note: Please, use some naming conventions. Class names should be capitalized (I already changed that in the above snippets, since it kills me...) and using myClass for a pointer to an instance of a class is plain disorienting.

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
  • This worked. Thank you, @Gabriele. I wasn't using the object scope properly, as you pointed out. I have one other question: What difference does changing 'nonatomic' to 'atomic' have in this instance? – Sparked Feb 16 '14 at 14:54
  • No significant difference. `atomic` will produce accessor methods which use an internal lock, resulting in a slower implementation. `atomic` alone doesn't guarantee thread-safety anyway, so in the 99% of cases `nonatomic` is much more sensible. – Gabriele Petronella Feb 16 '14 at 14:57
0

First, the double nil for the argument otherButtonTitles is "problematic". Reduce it to one.

Second, make sure the button and the object is properly retained by your calling class.

Finally, make sure via NSLog or breakpoints that the objects in question (custom object, button) are not nil.

Mundi
  • 79,884
  • 17
  • 117
  • 140
  • Out of interest why is the double `nil` problematic? – Paul.s Feb 16 '14 at 14:28
  • I would think that a nil terminated list is expected. The behaviour with two `nil`s is undefined. Problematic, ugly, unnecessary, distracting - remove. – Mundi Feb 16 '14 at 14:30
  • Yup I'll take the `undefined behaviour` argument. – Paul.s Feb 16 '14 at 14:31
  • `nil` terminated lists terminate as soon as the first `nil` is encountered, ignoring the rest. I agree that is ugly, unnecessary and distracting, but it is not problematic. – Gabriele Petronella Feb 16 '14 at 14:33
  • @GabrielePetronella this is why I questioned the comment originally but I think Mundi hit the nail on the head it's undefined, therefore it's not safe. If it works now it may not in future – Paul.s Feb 16 '14 at 14:41
  • @Paul.s well yes and no. Methods taking `nil`-terminated lists are parameters use `va_arg()` internally to extract their arguments in a loop. Such loop is interrupted whenever the parameter is not of the type expected by `va_arg()`, i.e. at the first `nil`. See https://developer.apple.com/library/mac/qa/qa1405/_index.html for an example. Again, this is not good quality code, but it's also far away from being problematic. – Gabriele Petronella Feb 16 '14 at 15:02