iOS memory management error - message sent to deallocated instance - iphone

I created many buttons before, but for some reason I'm having trouble creating a simple button.
In my viewDidLoad method I created a very basic button:
_button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_button.frame = CGRectMake(0, 0, 100, 25);
[_button addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_button];
- (void)buttonClicked:(id)sender
{
NSLog(#"%#", sender);
NSLog(#"Download issue");
}
But for some reason when I click on it I'm just getting an error
* -[DownloadButtonViewController performSelector:withObject:withObject:]: message sent to deallocated instance 0x6ac2af0
I have no idea what's going wrong as the code is exactly the same as every button I created before ... (probably just having a bad day ...)

Your view controller itself is being deallocated. Maybe you're using ARC and you don't have a strong reference to your view controller, so it's deallocated immediately after creation.

I have just Copy pasted your code and it's working fine here,it might be possible you are realeasing it some where by mistake because that object is Autoreleased it self and H2CO3 as said is also true.

Related

UIBarButtonItem created using initWithCustomView doesn't trigger action

I'm updating some old code, and to make more room in a toolbar, I'm converting the Buttons from test to images. An example of the new and old code in loadView is this:
// New code, doesn't work.
UIButton *toggleKeyboardBtn = [UIButton buttonWithType:UIButtonTypeCustom];
toggleKeyboardBtn.bounds = CGRectMake( 0, 0, showKeyboardImage.size.width, showKeyboardImage.size.height );
[toggleKeyboardBtn setImage:showKeyboardImage forState:UIControlStateNormal];
UIBarButtonItem *toggleKeyboardItem = [[UIBarButtonItem alloc] initWithCustomView:toggleKeyboardBtn];
[toggleKeyboardItem setTarget:self];
[toggleKeyboardItem setAction:#selector(toggleKeyboard:)];
// Original code, works jut fine.
UIBarButtonItem *setupItem = [[[UIBarButtonItem alloc] initWithTitle:#"Setup" style:UIBarButtonItemStyleBordered target:[UIApplication sharedApplication].delegate action:#selector(showSetupView:)] autorelease];
My new code is copied from Cannot set action on UIBarButtonItem, and I'm fairly certain that I'm not making their mistake since my text button is working just fine.
showSetupView() is in my AppController.m file, and the setup screen appears and disappears as the button is pressed.
toggleKeyboard(), OTOH, is in the same file as the loadView() routine, and currently consists of this code:
//- (void)toggleKeyboard {
- (IBAction)toggleKeyboard:(id)sender {
NSLog(#"Entering toggleKeyboard()...");
hiddenKeyboard = !hiddenKeyboard;
[self prepareToolbarsAndStatusbar];
}
Needless to say, although I see the button-press animation, I never see the NSLog message. And one last observation, made by accident. Changing the setAction selector to this:
[toggleKeyboardItem setAction:#selector(noSuchRoutine:)];
compiles cleanly, possibly indicating that my routine name is being ignored for some reason.
Anyone have any ideas? Thanks.
I found the answer! In button action not responding iphone, it's said that the action and target need to be set on the UIButton, not the UIBarButtonItem. I don't know if that's new with the latest version of Xcode, but I guess it is since other questions (such as the one I mention above) use a different technique. Here's my new code:
UIButton *toggleKeyboardButton = [UIButton buttonWithType:UIButtonTypeCustom];
toggleKeyboardButton.bounds = CGRectMake( 0, 0, keyboardAddImage.size.width, keyboardAddImage.size.height );
[toggleKeyboardButton setImage:keyboardAddImage forState:UIControlStateNormal];
[toggleKeyboardButton addTarget:self action:#selector(toggleKeyboard) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *toggleKeyboardItem = [[UIBarButtonItem alloc] initWithCustomView:toggleKeyboardButton];
//[toggleKeyboardItem setTarget:self];
//[toggleKeyboardItem setAction:#selector(toggleKeyboard:)];
Though its too late, but for future references, I would like to quote apple docs for the method,
- (instancetype)initWithCustomView:(UIView *)customView;
The bar button item created by this method does not call
the action method of its target in response to user interactions.
Instead, the bar button item expects the specified custom view to
handle any user interactions and provide an appropriate response.
Did you try
-(void)toggleKeyboard
and
[toggleKeyboardItem setAction:#selector(toggleKeyboard)]; without :
and it made any difference? Is the method declared in the interface file?

Unrecognized Selector Sent To Instance, iPhone Error

I get the error
"012-02-10 13:54:52.570 HelloWorld[14275:10103]
-[HelloWorldViewController buttonPressed]: unrecognized selector sent to instance 0x6cc0c50 2012-02-10 13:54:52.572 HelloWorld[14275:10103]
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[HelloWorldViewController
buttonPressed]: unrecognized selector sent to instance 0x6cc0c50'".
This is the offending text:
-(void)buttonPressed:(id)sender {
UIButton *button = (UIButton*)sender;
NSString *text = [button titleForState:UIControlStateNormal];
NSLog(#"%#",text);
}
I know this because if I change the code to this:
-(void)buttonPressed {
NSLog(#"Button Pressed");
}
It then works correctly.
However I need the text from the component that sent the message. The components are not drag and dropped with IB. They are allocated, initialized and placed in the loadView method. To each of my buttons I have added buttonPressed as the action listener.
The error unrecognized-selector could be due to a missing :.
[yourbutton addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
instead of
[yourbutton addTarget:self action:#selector(buttonPressed) forControlEvents:UIControlEventTouchUpInside];
In the first case you call -(void)buttonPressed:(id)sender. In the second instead you call -(void)buttonPressed.
But if you provide more code for your UIButton, it could be simpler to understand what is going on.
In the first (not working) case you have -(void)buttonPressed:(id)sender and in the second (working) you have -(void)buttonPressed. Obviously your button calls the function without argument.
that could be the case when you have added the #selector(buttonPressed:) for button touch event, you have forgotten to put the : with method name.
you can check for the same.

Programmatically adding switch to scrollview throws exception

I am using storyboards and have dragged a scrollview window onto a view. In my code I am programmatically creating a switch object that is somehow not being initialized correctly. The switch appears on the view correctly but whenever I click the switch, an exception is thrown saying
"unrecognized selector sent to instance 0x6a786f0'"
I have also attempted to edit the On/Off text to Yes/No and accessing the switch also throws the same exception. Clearly I have missed something in creating my switch and setting the correct delegates or whatever.
My code to create the switch is..
UISwitch *switchControl = [[UISwitch alloc] initWithFrame:CGRectMake(x, y, 60, 20)];
[switchControl addTarget:inputsView action:#selector(actionSwitch:) forControlEvents:UIControlEventTouchUpInside];
[switchControl setBackgroundColor:[UIColor clearColor]];
//[(UILabel *)[[[[[[switchControl subviews] lastObject] subviews]
// objectAtIndex:1] subviews] objectAtIndex:0] setText:#"Yes"];
//[(UILabel *)[[[[[[switchControl subviews] lastObject] subviews]
// objectAtIndex:1] subviews] objectAtIndex:1] setText:#"No"];
[inputsView addSubview:switchControl];
inputsView is the name of my UIScrollView that I created in my .h file.
I should note, when the exception is called on clicking the switch, in the error the 'reason' is reason: '-[UIScrollView actionSwitch:]. When the error is called by trying to adjust the text, the 'reason' is reason: '-[UIImageView setText:]
Any help on what I am missing would be great.
Thanks
The exception is correct, UIScrolView does not have a method actionSwitch:. The target parameter in addTarget: is the object you want to receive the selector: argument.
If your posted code is in the class that has the actionSwitch: method then you would use self as the target, like so:
[switchControl addTarget:self action:#selector(actionSwitch:) forControlEvents:UIControlEventTouchUpInside];
And as a side note. For a UISwitch you generally want your method called for UIControlEventValueChanged, that way if the user just touches the switch but doesn't "switch" it your method won't be called.
Edit in response to: "I just tried changing to 'self' for the UISwitch and the error still occurs. I haven't created an actionSwitch method."
Yes, your application would still crash because whatever you pass in as the target must implement the selector/method passed in as the selector.
The view controller is the ideal place to implement this method. A very standard implementation of this event target would look like:
-(void)actionSwitch:(UISwitch *)theSwitch{
if (theSwitch.isOn){
// Switch was switched on respond accordingly
}
else {
// Switch was switched off respond accordingly
}
}

How can I safely memory manage a button that I want to remove from the UI?

I'm making an app that is kinda like iBooks. There is some stuff on the screen, each item represented by a small thumbnail. I want the user to be able to delete the items much like when you tap the "Edit" button in iBooks - an X comes up, and the item is deleted. I'm using the delegation pattern to handle all of this, so here is some code:
// Button is created in CustomView.h class
UIImage *deleteImage = [UIImage imageNamed:#"delete.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
self.deleteButton = button;
[self.deleteButton setImage:deleteImage forState:UIControlStateNormal];
[self.deleteButton addTarget:self action:#selector(deleteIt) forControlEvents:UIControlEventTouchUpInside];
// Here's what's called when the delete button is pushed
- (IBAction)deleteMap {
[self.customViewDelegate itemWasDeleted:self];
}
// And here's the implementation of that method, in a View Controller
- (void)itemWasDeleted:self:(CustomView*)customView {
// delete domain object
// . . .
[self.collectionOfCustomViews removeObject:customView];
[customView removeFromSuperview];
}
The problem with this code is that I get a Bad Access Exception. Via NSZombie, looks like this:
* -[UIButton _unhighlight]: message sent to deallocated instance 0x5f4a740
I guess what's happening is that when my target-action implementation is called, it's not yet safe to release the button, as I'm doing in my delegate method. So my question is, what's a better approach to doing this so that may app doesn't crash? I'd like to know the cleanest approach possible.
If collectionOfCustomViews was the only object retaining customView then you released it when you removed it, so it can't respond to removeFromSuperview.

uiimage and uibutton release problem?

I've been coding for a while now in objective-c and am comfortable with it... but one thing eludes me. Memory management. I'm releasing as I think is correct, but this bit of code is throwing a "EXC_BAD_ACCESS" and crashes the app.
When I comment out and DON'T release the button and image, it works fine. The function is called to read through an array of image filenames.
for (x=items_start;x<items_stop;x++) {
UIButton *button;
UIImage *buttonImage;
buttonImage = [UIImage imageNamed:[NSString stringWithFormat:#"%i.png", x]];
button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tag = x;
[button setImage:buttonImage forState:UIControlStateNormal];
[button addTarget:self action:#selector(duplicateImage:) forControlEvents:UIControlEventTouchUpInside];
[viewBasicItems addSubview:button];
[buttonImage release];
[button release];
}
any ideas? Like i said, when I comment out the last two lines (releasing the button and image) it works OK. Is this normal or should I be able to release them?
Note: I have remove a fair bit of other code to show this example in a smaller chunk!
The instance of button is autoreleased:
button = [UIButton buttonWithType:UIButtonTypeCustom];
You're using the convenience method +buttonWithType: instead of an alloc/init pair. So your app will crash here, as well:
[button release];
Either remove that -release statement or use alloc/init to instantiate the button view.
I would recommend you use alloc/init since you're doing all of this stuff inside a for loop. You could be building up a lot of objects in that loop that need to be autoreleased. It's probably better to manually allocate memory and release it.
And do read Apple's memory management guide.
buttonImage = [UIImage imageNamed:[NSString stringWithFormat:#"%i.png", x]];
Your buttonImage object is autoreleased so you must not release it in your function.
From Memory management guide:
You only own objects you created using
a method whose name begins with
“alloc” or “new” or contains “copy”
(for example, alloc, newObject, or
mutableCopy), or if you send it a
retain message.
Edit: As Alex points your button object is autoreleased also.
You have three choices:
Use alloc/init
for example;
NSString *imagePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:[NSString stringWithFormat:#"/%i.png",x]];
buttonImage = [[UIImage alloc] initWithContentsOfFile:imagePath];
and release it after you are done
[buttonImage release];
Or use retain/copy
buttonImage = [[UIImage imageNamed:[NSString stringWithFormat:#"%i.png", x]] retain ];
and release it
[buttonImage release];
Or use Autoreleased objects like you did but do not release it, because they will be released automatically.
You should read Apple memory management guide like others said.
Ok, the basics.
Most methods that create objects return objects that are allocated, then "autoreleased."
The autorelease call adds your object to the "autorelease pool", which means that they will receive a release call next time your app visits the event loop.
When you use auto released objects, you can use them, then forget about them. They get released automatically.
The exception, as others have said, is calls that have "init" or "new" in the name, or calls to "copy" methods. These methods return objects that have not been auto released. The owner of these objects needs to release or autorelease these objects in order for them to be deallocated, and not cause a memory leak.
In your example code, you create your buttonImage and button objects using the calls +imageNamed and +buttonWithType.
These are class calls that return an object of the desired type. Since they do not contain "init" or "new" in their names, the objects that they return are already autoreleased, so you should NOT release them.
You pass the image you create to the button, so the button retains the image. You then pass the button object to your viewBasicItems object with the -addSubview call, so the view retains the button.
Thus, you should not do anything else. The button will retain the image, and the view will retain the button.
I hope that helps.
it is not a good method of using [UIButton ButtonWithType:]..coz you cant relase the object..instead of this jus use
`for (x=items_start;x<items_stop;x++) {
UIButton *button;
UIImage *buttonImage;
buttonImage = [UIImage alloc] initWithContentOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingString:[NSString stringWithFormat:#"/%i.png",x]]];
button = [[UIButton alloc] init];
button.tag = x;
[button setImage:buttonImage forState:UIControlStateNormal];
[button addTarget:self action:#selector(duplicateImage:) forControlEvents:UIControlEventTouchUpInside];
[viewBasicItems addSubview:button];
[buttonImage release];
[button release];
}
`
Fixing in your code as following:
for (x=items_start;x<items_stop;x++) {
UIButton *button;
button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tag = x;
[button setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%i.png", x]]
forState:UIControlStateNormal];
[button addTarget:self action:#selector(duplicateImage:)
forControlEvents:UIControlEventTouchUpInside];
[viewBasicItems addSubview:button];
[button release];
}