Object deallocated in ARC mode - ios

The object is deallocated in ARC mode and causes crash. My code below;
BlockAlertView* alert = [BlockAlertView alertWithTitle:title message:message];
[alert setCancelButtonWithTitle:NSLocalizedString(#"No", #"No Button") block:nil];
[alert addButtonWithTitle:NSLocalizedString(#"Yes", #"Yes Button") block:^{
//Do Something
}];
[alert show];
It appears the correct alert view(which is custom UIView) but when I click one of the button it crashes.
The crash log;
2015-04-07 22:28:17.065 Test[3213:781570] <BlockAlertView: 0x16bb4160>
2015-04-07 22:33:01.746 Test[3213:781570] *** -[BlockAlertView performSelector:withObject:withObject:]: message sent to deallocated instance 0x16bb4160
Here is the source code of BlockAlertView;
BlockAlertView on Github
For now I can't estimate any of the clues for this and makes me old.
Any input will be much appreciated!

Presumably, that code worked correctly before it was converted to ARC.
To fix it, you'll need to create a strong reference to self in the -show method, and release this reference in -dismissWithClickedButtonIndex:animated: (where you see [self autorelease] commented out).
You can do this with a simple instance variable:
id _selfReference;
Assign self to _selfReference in -show:
- (void)show
{
_selfReference = self;
...
and then set _selfReference to nil in -dismissWithClickedButtonIndex:animated: in the two places where you see [self autorelease] commented out.

Assign your alert object to live somewhere beyond your current function. One simple possibility is to just make it an instance variable. If that's not practical, create an instance NSMutableArray *retainedItems; which you allocate/init, and stuff this into.

Looks like a design flaw in that project. The class is poorly named BlockAlertView as it's not actually a subclass of UIView. If it were a view and it was added to the view hierarchy then the view hierarchy would ensure it stayed alive whilst being viewed. As it is the view is kept alive but the object that created the view BlockAlertView is not held onto by anything and by the time the actions are called BlockAlertView is long gone.
This will require you to keep a strong ivar around to reference this "controller" object and it would be sensible to nil out that ivar in the completion blocks.
BlockAlertView *alertController = [BlockAlertView alertWithTitle:title message:message]; {
[alertController setCancelButtonWithTitle:NSLocalizedString(#"No", #"No Button") block:nil];
__weak __typeof(self) weakSelf = self;
[alertController addButtonWithTitle:NSLocalizedString(#"Yes", #"Yes Button") block:^{
//Do Something
weakSelf.alertController = nil;
}];
[alertController show];
}
self.alertController = alertController;

Related

How to know if my keywindow.rootViewController is an Alert?

I'm having a problem to find out if my keywindow.rootViewController is an UIAlertController object. This must be a very simple thing to do, but I don't know what is wrong with my code:
UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
if( [rootViewController isKindOfClass:[UIAlertController class]]){
// Do something
}
Why I never enter in that IF STATEMENT, even when I can see on the debug that the view of this controller is an _UIAlertControllerView*? There is any other way to check if my key window is an Alert?
Thanks.
I assume what you want to do is be able to check if an alert is showing from any view controller in your application, but you're not sure which controller is displaying the alert and don't have access to the alert itself.
I've had to deal with this a few times. In previous versions of iOS you were able to iterate through all the subviews of a window to check if an UIAlertView was on the top, but with the changes to alerts in iOS8 this not longer works because all the delegates are deprecated and apple now recommends you use UIAlertController instead of UIAlertView. In any case, all the techniques were dependent on using certain versions of iOS and I've found them to be extremely unreliable.
What I use now is a singleton that keeps track of how many alerts are displaying. The singleton has a method that returns the current number of alerts displaying, a method for adding one, and a method for subtracting one.
This is implemented by adding one to the singleton when you present the UIAlertController:
[self presentViewController:alert animated:YES completion:^(){
AlertSingleton *muhInstance = [AlertSingleton sharedInstance];
[muhInstance addOne];
//Anything else for completion
}];
And then subtracting one with every possible action choice you add to the alert like this:
UIAlertAction *myAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
AlertSingleton *muhInstance = [AlertSingleton sharedInstance];
[muhInstance removeOne];
//Any Other alert actions
}];
Now you can know anywhere in your application if an alert is showing by checking if the count in the singleton is greater than zero like this:
if ([[AlertSingleton sharedInstance] alertCount] > 0) {
//There is an alert showing
//Your code here
}
I've found this technique to be very reliable for tracking alerts.

Understanding a strong pointer and out of scope

I wanted to understand the following scenario
-(void) foo
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Could not save file"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
//This is called when ok is pressed
}
In the above code a UIAlertView strong pointer is created and a delegate to self is assigned. The reason I called it a strong reference pointer is because it is created in scope and will go out of scope when its reference count goes to 0. I believe the reference count goes to 0 when the method foo ends then why am I still getting a callback at clickedButtonAtIndex ? I was under the assumption that we would not get a callback because the destructor of the alertView instance would have been called as soon as the method foo ended.
I believe the reference count goes to 0
You believe wrong. When you say [alert show], you hand the alert object over to Cocoa, which retains it; otherwise there would be no alert view to appear on the screen! That alert view has a reference (which is actually weak) to you (self). And Cocoa thus is able to hand the very same alert view back to you in the delegate callback; the alert is still alive because Cocoa is still retaining it, and you are still alive because you are still alive, so the reference to self works as the target of the callback.
Also, I can't quite tell whether you grasp that as soon as you say [alert show], the code does not pause - it goes right on, immediately. Thus the first method is over before the alert actually appears on the screen. Again, this works because the alert has been handed over to Cocoa, which retains it and takes care of showing it on the next runloop. None of your code is running while the alert is present on the screen.
A completely parallel situation is
MyViewController* vc = [MyViewController new];
[self.presentViewController:vc animated:YES completion:nil];
The code ends, so why doesn't MyViewController vanish in a puff of smoke? Because presentViewController hands it over to Cocoa, which inserts it into the view controller hierarchy and retains it.

UIAlertView after MOC background save works iOS 7 not iOS 8

I have a parent detail view controller that provides common custom functions to child detail views.
The parent includes two custom functions.
One function triggers a background save to two NSManagedObjectContexts that saves a main MOC immediately to free up UI, then saves a private MOC. Fairly standard setup pre iOS 8.
The following function presents a UIAlertView to confirm the save was successful. This includes code to automatically dismiss after a set amount of time (about half a second).
This all works fine running iOS 7, both on device and on simulator.
This causes a crash when running iOS 8, both on device and on simulator.
The problem exists for only one of the five child detail views. Following a detailed side by side comparison I confirm that these each have identical code blocks and methods.
I have break points inserted into the two custom functions. The save works fine, but the code crashes after trying to present the UIAlertView, as mentioned only when running iOS 8. The debugger steps into machine code that I do not understand. The attempted save does not persist.
If I comment out the alert view, the save persists, but obviously I no longer have the alert view for the user.
Any suggestions?
UPDATE
Think I may have found a solution... of sorts... not definite yet...
UIAlertController and this article by NSHipster
Augmenting the UIAlertView code in my parent detail view controller resolves the problem.
Within my message method I now check whether iOS responds to the UIAlertController class, and if it does instantiate a UIAlertController, otherwise instantiate a UIAlertView.
- (void)message {
...other code...
if ([UIAlertController class]) { //checking whether iOS responds to the UIAlertController class
UIAlertController *alert = [UIAlertController alertControllerWithTitle:titleComplete
message:messageComplete
preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController:alert animated:YES completion: ^(void){
[self performSelector:(#selector(dismissAlertController:)) withObject:alert afterDelay:durationMessageCompleteSave];
}];
// [self dismissViewControllerAnimated:YES completion:nil];
// display time on screen too short using just the dismissViewController above,
// so add into completion handler in call to presentViewController...
} else {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:titleComplete
message:messageComplete
delegate:self
cancelButtonTitle:nil//self.localisedAlertButtonRemain
otherButtonTitles:nil];//buttonOther, nil];
[alertView setTag:010];
[alertView show];
[self performSelector:(#selector(dismissAlertView:)) withObject:alertView afterDelay:durationMessageCompleteSave];
}
}
...
- (void)dismissAlertView:(UIAlertView *)alert {
[alert dismissWithClickedButtonIndex:0 animated:NO];
}
- (void)dismissAlertController:(UIAlertController *)alert {
[alert dismissViewControllerAnimated:YES completion:nil];
}

when i set delegate.self in uialertview application is crash in arc

UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Order"
message:#"Order Successfully Discontinued."
delegate:self
cancelButtonTitle:nil
otherButtonTitles: #"Ok",nil];
//[alertView performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
alertView.tag=TAG_DEV;
[alertView show];
-(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex{
if(alertView.tag==TAG_DEV)
{
if(buttonIndex==0)
{
}
else
NSLog(#"Here");
}
}
This crashes. How can I fix it?
When you crash on a "objc_msgSend()" you are most likely sending a messageto an already-freed object. Or you have a pointer, which is correct, but something have changed the objects contents. Another cause can be the use of a dangling pointer that once pointed to the memory now occupied by your object. Occasionally objc_msgSend crashes because a memory error changed the runtime's own data structures, which then would cause trouble in the receiver object itself.
In this situation you need to check wheter or not, the delegate of the UIAlertview has been released after presenting, so when the alert is dismissed, it is not sending a message to it's delegate, which may be nil. An other option is that the UIViewController that presents the alert view is released after it is presented. Please check if the alert delegate is not released after it is presented.

ARC releases my object before completion handler block is invoked

I'm wrapping an UIAlertView inside a regular NSObject to allow completion handler blocks instead of the delegate pattern.
The problem is that I allocate a local instance of my object, that internally creates an UIAlertView and assigns its delegate to the object itself. When the alert is shown and the user taps a button, the apps crashes with an EXC_BAD_ACCESS because ARC has released my object and the delegate of the alert is that object.
How could I handle this situation? I saw that a solution is to qualify the local variable with __block and use the object itself inside the completion block, but that doesn't work.
By the way, if I subclass 'UIalertView' instead of wrapping it, it works, but documentation says that alert subclassing is not recommended, so I prefer to resolve this issue.
You can associate your object with the alert view like so:
#import <objc/runtime.h>
...
- (void)showAlertView
{
UIAlertView *alertView = [[UIAlertView alloc] initWithWhatever:...];
objc_setAssociatedObject(alertView, _cmd, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[alertView show];
}
That will retain your object, and then release it again when the alertView is dealloced. Your object mustn't retain the alertView, or you'll have a retain cycle.

Resources