App crashes in release mode, but doesn't crash when zombies enabled - ios

I'm trying to figure out what is wrong with my app. It crashes EXC_BAD_ACCESS in release mode, but when I try checking for zombies, it doesn't crash via instruments. Without fail, I turn off zombie detection and it crashes.
When it does crash the only thing I can tell is that the very latest call in the vm allocation shows this viewDidLoad. So I'm wondering if there's something wrong here?
- (void)viewDidLoad
{
[super viewDidLoad];
//load abstract
if ( self.abstractId > 0 ){
[self startQuery:#selector(getAbstractWithId:)];
}
//setup nav bar
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStylePlain target:nil action:nil];
[self.view addSubview:[self makeFavoriteButton]];
//add link attributes
self.linkAttributes = #{NSForegroundColorAttributeName: [UIColor colorWithHexString:emaGreen],
NSUnderlineColorAttributeName: [UIColor lightGrayColor],
NSUnderlineStyleAttributeName: #(NSUnderlinePatternSolid)};
//create text view
UITextView *tv = [[UITextView alloc] initWithFrame:self.view.frame];
tv.editable = NO;
tv.textAlignment = NSTextAlignmentLeft;
tv.text = #" ";
tv.backgroundColor = [UIColor whiteColor];
tv.scrollEnabled = YES;
tv.dataDetectorTypes = UIDataDetectorTypeLink;
tv.linkTextAttributes = self.linkAttributes; // customizes the appearance of links
tv.delegate = self;
// set the scroll indicators between nav and tabs
tv.scrollIndicatorInsets = UIEdgeInsetsMake(0,
0,
CGRectGetHeight(self.tabBarController.tabBar.frame),
0);
//add to property and view
self.tv = tv;
[self.view addSubview:tv];
//Create spinner view
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
hud.mode = MBProgressHUDModeIndeterminate;
self.hud = hud;
}
What other debugging options do I have here?
Thanks!

I'm going to guess it's this line:
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStylePlain target:nil action:nil];
To quote the documentation for the parameters of UIControl:
target: The target object—that is, the object to which the action message is sent. If this is nil, the responder chain is searched for an object willing to respond to the action message
action: A selector identifying an action message. It cannot be NULL
Strangely, this is not specified for UIBarButtonItem's initializer, but I see no reason why it shouldn't be true there as well, unless that class actually checks these parameters for null and behaves accordingly.
Perhaps your bar button item is trying to access the null selector to send it and is crashing there, or is trying to send it to some object that has already been released. This might only be brought on by some optimization -- for example, perhaps in release mode the button grabs a pointer to the function that would be called by a message send, instead of sending the message, as an optimization.
At the very least, passing nil there seems like mistake.

Thanks for the comments. Strangely, I finally got a console output in zombies, with the following KVO error message received but not handled.
Which I was able to track down to an observer that was not removed when dealloc'd. Worst bug ever. ugh. Thanks for the help!
-(void)dealloc
{
[self.queryQueue removeObserver:self forKeyPath:#"operations"];
}

Related

ios remote keyboard window

In iOS 9, I see a new window appearing in my app that I didn't see before. An image is below. From walking the view tree, I suspect it may be coming from the UIRemoteKeyboardWindow -- but I don't know that. What is it, and what do I have to do to keep it from appearing?
EDIT: As a commenter pointed out, this is tied to the inputView, i.e. the keyboard. I don't want a keyboard and so disabled it by calling
self.inputView = [[UIView alloc]initWithFrame: CGRectZero];
That did kill the keyboard, but there is still the accessory. I've tried similar tricks to kill the accessory; none of them have worked, yet. Calling self.inputAccessoryView is returning nil, which doesn't help.
This got rid of it:
-(void) killAccessory {
UIView* input = self.inputView;
UIView* parent = input.superview;
parent.hidden = YES;
}
-(BOOL) becomeFirstResponder {
BOOL r = [super becomeFirstResponder];
[self killAccessory];
return r;
}
You can use the following code :
textField.inputAssistantItem.leadingBarButtonGroups = [[NSArray alloc] init];
textField.inputAssistantItem.trailingBarButtonGroups = [[NSArray alloc] init];
the textField use above is the textField which you tap to edit.

Modal view with Web view makes an app crash in iOS7

I have been developing in Objective-C for two months, so I am quite new to this language and iOS environment. I am updating to iOS7 an app that is working fine for iOS6.
I am getting the next error when a modal view with a web view inside is presented, only in iOS7 and this is working in iOS6. There is a URL request inside but I cannot find what is causing the error.
'-[__NSMallocBlock__ absoluteURL]: unrecognized selector sent to instance 0x16e8b020'
This is the viewWillAppear method on the modal view controller:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!self.webView.request) {
//THE NEXT LINE THROWS THE ERROR
NSURLRequest *req = [[NSURLRequest alloc] initWithURL:self.initialURL];
[self.webView loadRequest:req];
}
}
Maybe I am doing something silly but really now I do not know where to look at.
If anyone has experienced something like that before, I will appreciate some help. Thanks in advance.
EDIT:
#interface MyViewController ()
#property (copy, nonatomic) NSURL *initialURL;
#end
#implementation MyViewController
- (id)initWithURL:(NSURL *)initialURL
{
self = [super init];
if (self) {
_initialURL = initialURL;
_webView = [[UIWebView alloc] init];
_webView.backgroundColor = [UIColor clearColor];
_webView.opaque = NO;
_webView.delegate = self;
[self.view addSubview:_webView];
self.modalPresentationStyle = UIModalPresentationFormSheet;
self.view.backgroundColor = [UIColor whiteColor];
}
return self;
}
Method call:
self.modalWebViewController = [[[MyViewController alloc] initWithURL:url] autorelease];
I assume that iOS calls absoluteURL on the self.initialURL object passed to the initWithURL: method. However, the object receiving this message is an NSMallocBlock, so there seems to be something wrong. I assume that your self.initialURL object should be of type NSURL. If so, this would indicate a memory management problem causing the pointer of self.initalURL to point to somewhere else in memory (not to the object you want it to point to).
You could try to run your app with NSZombiesEnabled which prevents any objects from being actually deallocated and instead warns you if a deleted object is still accessed.
You can activate NSZombies in the scheme to run your app (click on the name of your app in Xcode's toolbar on the upper right and choose "Edit Scheme..." from the pop-up menu). In the run-configuration in the "Diagnostics" tab there is a checkbox for activating Zombie objects.

Callout button crashes app - MapKit

UICalloutView willRemoveSubview:]: message sent to deallocated instance;
This only happens when I tap the callout button, but not on the first and not on the 2nd tap, but from the 3rd tap. So I tap the custom AnnotationView, callout pops, thats good. I tap it again, callout pops, all good. I tap another one, boom crash with that message. It only happes if is set the right accesoryview to be a button.
One key aspect to keep in mind..only happens in iOS 6... (go figure).
I am really stuck on this one – some help would be appreciated.
if ([annotation isKindOfClass:[RE_Annotation class]])
{
RE_Annotation *myAnnotation = (RE_Annotation *)annotation;
static NSString *annotationIdentifier = #"annotationIdentifier";
RE_AnnotationView *newAnnotationView = (RE_AnnotationView *)[mapViews dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];
if(newAnnotationView)
{
newAnnotationView.annotation = myAnnotation;
}
else
{
newAnnotationView = [[RE_AnnotationView alloc] initWithAnnotation:myAnnotation reuseIdentifier:annotationIdentifier];
}
return newAnnotationView;
}
return nil;
Also, this is my initwithannotation method:
- (id)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if(self)
{
RE_Annotation *myAnnotation = annotation;
self = [super initWithAnnotation:myAnnotation reuseIdentifier:reuseIdentifier];
self.frame = CGRectMake(0, 0, kWidth, kHeight);
self.backgroundColor = [UIColor clearColor];
annotationView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"map_pin_pink.png"]];
annotationView.frame = CGRectMake(0, 0, kWidth - 2 *kBorder, kHeight - 2 * kBorder);
[self addSubview:annotationView];
[annotationView setContentMode:UIViewContentModeScaleAspectFill];
self.canShowCallout = YES;
self.rightCalloutAccessoryView = [[UIButton buttonWithType:UIButtonTypeInfoLight] retain]; ///if i take it out it doesnt crash the app. if i leave it it says that message
}
return self ;
}
In the initWithAnnotation method, there is this line:
self.rightCalloutAccessoryView =
[[UIButton buttonWithType:UIButtonTypeInfoLight] retain];
///if i take it out it doesnt crash the app. if i leave it it says that message
By "it", you must be referring to the retain and this implies the app is not using ARC.
Based on that, you should make the following corrections:
In viewForAnnotation, you need to autorelease the view when you alloc+init it otherwise there's a leak:
newAnnotationView = [[[RE_AnnotationView alloc]
initWithAnnotation:myAnnotation
reuseIdentifier:annotationIdentifier] autorelease];
In initWithAnnotation, remove the retain when creating the callout button since buttonWithType returns an auto-released object (and you don't want to over-retain it):
self.rightCalloutAccessoryView =
[UIButton buttonWithType:UIButtonTypeInfoLight];
Another possibly unrelated issue is that in initWithAnnotation, the code is calling super initWithAnnotation twice. This seems unnecessary and may be harmful. Remove the second call.
The above changes at least fix the issues with the code shown. However, there may be other, similar memory-management related issues in the rest of the app that may still cause crashes. Check and solve all issues reported by Analyzer.
Regarding the fact that "it only happens in iOS 6": iOS 6 may be less forgiving of memory-management errors or an internal change in the SDK may be exposing or manifesting these errors earlier. Regardless, the above fixes are necessary.
An unrelated point is there should be no need to manually create a UIImageView and addSubview it to the annotation view. You can just set the annotation view's image property.

What's the correct way to create and show an UIBarButtonItem programmatically?

I'm having a problem: the app I'm working on crashes every time an UIBarButtonItem is tapped.
I'm creating the button in the viewDidLoad method of my main ViewController:
UIBarButtonItem *settingsButton = [[UIBarButtonItem alloc] initWithTitle:#"Settings" style:UIBarButtonItemStyleBordered target:self action:#selector(showSettings)];
self.navigationItem.leftBarButtonItem = settingsButton;
The showSettings method only contains an NSLog():
- (void)showSettings {
NSLog(#"ciao");
}
The button is correctly showed in the navigation bar, but whenever is tapped the application crashes with an exc_bad_access message.
What am I doing wrong?
EDIT: ARC is ebabled and I'm running the code in the iOS 5.1 simulator.
Changes:
UIBarButtonItem *settingsButton = [[UIBarButtonItem alloc] initWithTitle:#"Settings" style:UIBarButtonItemStyleBordered target:self action:#selector(showSettings:)];
- (void) showSettings:(UIBarButtonItem *)sender
{
}
As mentioned by the ref doc about selector:
If an object receives a message to perform a method that isn’t in its
repertoire, an error results. It’s the same sort of error as calling a
nonexistent function. But because messaging occurs at runtime, the
error often isn’t evident until the program executes.
So make sure your method is in the scope of your viewcontroller. I will start by checking you don't have any typo. If your method don't take any argument don't use : and make sure you have declared your method in your class description.
To create a uibarbutton programatically. this example is for custom uibarbutton
UIButton *timebutton = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 90, 40) ];
[timebutton setTitle:timeString forState:UIControlStateNormal];
[timebutton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
UIBarButtonItem *rightBarButtonItem1 = [[UIBarButtonItem alloc]
initWithCustomView:timebutton];
[rightBarButtonItem1 setTintColor:[UIColor blackColor]];
//set the action for button
rightBarButtonItem1.action = #selector(navigationMethod:);
self.navigationItem.rightBarButtonItem = rightBarButtonItem1;
//Method declaration
-(IBAction)navigationMethod:(id)sender{
// action
}
Hope this helps you

iPhone: How can I have my refresh UIBarButtonItem change to UIActivityIndicator while task is running?

I have a refresh button that executes a function to initiate a request and receive a response. While it's parsing the response I'm changing my last updated text to keep the user informed on what is going on. I'm hoping though to have the refresh button become the uiactivityindicator while the "procedure" is running. How can I accomplish this? Here is a screenshot of my storyboard to help you get and idea of the setup. Let me know if I can provide anything else. Thanks!
Image below shows how I created the bottom bar through the simulated metrics drop down list to the right.
EDIT:
Here is the altered code I used from the marked answer.
// Create UIActivityIndicator UIBarButtonItem
UIActivityIndicatorView *activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
[activityView startAnimating];
UIBarButtonItem *loadingView = [[UIBarButtonItem alloc] initWithCustomView:activityView];
// Toolbar with LoadingView and LastUpdatedTitle with Flex Spacing
[self.navigationController.toolbar setItems:(NSArray *)[NSArray arrayWithObjects: loadingView, self.flexSpaceOne, self.lastUpdated, self.flexSpaceTwo, nil]];
When I wanted to change it back I simply "reset" my items using the same setItems:arrayWithObjets: method. Except that time I would change out the loadingview with self.refreshButton.
Define some ivars to access your toolbar and its items:
NSMutableArray *toolbarItems;
IBOutlet UIToolbar *toolbar;
Then, in your method where you kick off the task,
UIActivityIndicatorView *activityView = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 25, 25)];
UIBarButtonItem *loadingView = [[UIBarButtonItem alloc] initWithCustomView:activityView];
[toolbarItems replaceObjectAtIndex:0 withObject:loadingView];
toolbar.items = toolbarItems;
Then do the reverse when it is done to add your refresh button again.
This is a good time to use blocks and start another thread.
- (IBAction)refresh:(id)sender
{
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[spinner startAnimating];
self.navigationItem.rightBarButtonItem = [[BarButtonItem alloc] initWithCustomView:spinner];
dispatch_queue_t request = dispatch_queue_create("request data", NULL);
dispatch_async(request, ^{
// call the method to request data
dispatch_async(dispatch_get_main_queue(), ^{
self.navigationItem.rightBarButtonItem = sender;
// populate the table with the data
});
});
dispatch_release(request);
}
You'll notice (of course) that I haven't put the spinner where you want it. That's because I'm not confident about how to access the tool bar items -- but hopefully this will put you on the right track.

Resources