I have an NSTimer displaying text over time. If a popOver shows up at the same time, the timer seems to just die. The method it's calling stops printing log statements, and my code is definitely never invalidating it.
Is this expected behavior? Should I never have a popOver when I'm running a timer?
Edit: By "at the same time" I mean, before the NSTimer has been invalidated by me (it's still displaying text), a user triggers a popOver, which prevents the NSTimer from continuing. I have methods surrounding invaliding a NSTimer which print log statements, and am invalidating it nowhere but that method. The log statements aren't displaying when a popOver happens.
Here's how I'm calling the popOver:
if ([self.popOverController isPopoverVisible]) {
[self.popOverController dismissPopoverAnimated:YES];
} else {
SubMenuViewController *popup = [[SubMenuViewController alloc] initWithStorySections:tmp_menu];
self.popOverController = [[UIPopoverController alloc] initWithContentViewController:popup];
popup.delegate = self;
[self.popOverController presentPopoverFromRect:self.textView.frame inView: self.backgroundView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
I don't THINK I'm doing any weird threading things with it, but it still interferes with my timer. I do notice that that the timer is already called before the popup is triggered, could that be doing anything?
A timer shouldn't be killed by a popover, but it does depend on the popover. If you have a custom made one that's not calling the main runloop, that might be your problem. You want to make sure the system gets time so it can call your timer call. Otherwise, put breakpoints on all your calls to invalidate the timer and see if one is getting called on you.
Related
So somehow my weakSelf variable is being deallocated before my block has a chance to execute. This only happens in one specific scenario, the other times I hit this block it works fine. Here's what my code looks like:
__weak typeof(self) weakSelf = self;
DBTEligibleAccountFetcher *accountFetcher = [[DBTEligibleAccountFetcher alloc] init];
NSArray *eligibleDepositAccounts = [accountFetcher fetchDepositEligibleAccounts];
if(eligibleDepositAccounts.count == 1) {
DBTDepositAmountLimitsHandler *limitChecker = [[DBTDepositAmountLimitsHandler alloc] init];
[limitChecker handleRequest:self.navigationController bankAccount:eligibleDepositAccounts.firstObject completionBlock:^(DBDepositCheckAccountLimits *limitDetails) {
containerController.limitDetails = limitDetails;
[weakSelf.navigationController handleNewRootPush:containerController withCompletion:completionBlock animated:YES];
}];
} else {
[self.navigationController handleNewRootPush:containerController withCompletion:completionBlock animated:YES];
}
By the time it gets to the weakSelf.navigationController... line, weakSelf is nil. I'm not sure how to even debug this or how the variable could get deallocated before it is even used.
Does anyone know any scenarios that could be going on right now? Or any tips on how I might go about debugging this issue?
Let's say you have a view controller, the user taps a button, therefore you send some information to the server asynchronously and you are prepared to handle when the request comes back. Meanwhile your user switches to another view and your view controller gets deallocated before your method returns.
First consider what you want to happen. Sometimes a request has become pointless if the view controller is gone. Say you have a site that translates German to French. If the translation comes back and your view controller is gone, you just ignore the result. But if you have to process the result, whether the view controller is there or not, then you need to rearrange your code so that it works even if the view controller is nil because the user switched to a different view.
I have Simple popOver controller in my project. i can dismiss it using action button with following code.
[[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
but sometimes app gets crash if i do something on that viewController
before dismiss it.
Example : There is a pickerView for selection and post button. but without posting it if i did some selection and cancel it im getting this error. its only happening occasionally. not everytime. if it happens onetime then it might happen everytime after that.
Can someone explain? is it memory leak or something?
I think have solved the problem . in that PopOver new window i was using locationManager and i was using just
[locationManager stopUpdatingLocation];
but i have added following thing as well
locationManager = nil;
no crash anymore
Sometimes if you have dealloc function as mentioned below, this issue may occur.
- (void)dealloc {
[_yourview release]; //don't do this
[super dealloc];
}
I hope this might help some one else
I'm currently developing an iPad book-style app, where I'm using a view controller to manage the main window, and then I use a number of page controllers equal to the number of pages in the book. All the page controllers inherit from a base class, PageController, where I defined the main methods used in every page, as well as the variables.
So, my view controller tracks the current page using an object of PageController type, and when I want to load another page, the view controller calls a method (transitionToNextPage), and this method returns the next page controller.
For example, if the current page is number 2, its class is Page02Controller, and the next page class is Page03Controller, which is returned from the Page02Controller.
The issue I've been fighting with, and to which I'm asking for some advice, is when I need to call the transitionToNextPage method, and the method returns when it's still doing some actions, like animating the transition (loading some frames, for example). For example, in the code below, I call the method transitionToNextPage and I start a timer to load some frames. However, the function returns right after the timer starts, and it counts for about 1 second.
- (PageController *)transitionToNextPage{
if ([self.timerAnimationAngel isValid]) {
[self.timerAnimationAngel invalidate];
self.timerAnimationAngel = nil;
}
if ([self.timerAnimationFeather isValid]) {
[self.timerAnimationFeather invalidate];
self.timerAnimationFeather = nil;
}
[super hideTransitionButtons];
[NSTimer scheduledTimerWithTimeInterval:1.f/(float)self.filenamesImagesTransition.count target:self selector:#selector(updateTransitionViews:) userInfo:nil repeats:YES];
self.imageViewTransition = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:[NSString stringWithMainBundle:self.filenamesImagesTransition[0]]]];
self.imageViewTransition.layer.zPosition = kZPositionTransition;
[self.mainView addSubview:self.imageViewTransition];
return [[Page02Controller alloc] initPageNumber:2 withView:self.mainView withViewController:self.mainController];
}
So far, I have all the code working as I intend, but I don't think I'm doing in the best way. What I'm doing is calling a method from the super class of page controller when the timer ends, as shown in the code below:
- (void)updateTransitionViews:(NSTimer *)timer{
static int indexImageTransition = 0;
if (indexImageTransition >= self.filenamesImagesTransition.count) {
[super clearAllViewsIncludeBackground:YES];
[timer invalidate];
timer = nil;
[super loadNextPage];
}
else{
self.imageViewTransition.image = [UIImage imageWithContentsOfFile:[NSString stringWithMainBundle:self.filenamesImagesTransition[indexImageTransition]]];
indexImageTransition++;
}
}
And the [super loadNextPage] calls a method in the view controller, and is defined in PageController (the super class) as:
- (void)loadNextPage{
SEL selector = NSSelectorFromString(#"loadNextPage");
if([self.mainController respondsToSelector:selector]){
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self.mainController performSelector:selector];
#pragma clang diagnostic warning "-Warc-performSelector-leaks"
}
else{
NSLog(#"Selector not found.");
}
}
The method transitionToNextPage is called from the view controller the following way:
- (IBAction)buttonNextPage:(id)sender {
NSLog(#"Next page button pressed.");
self.currentPage = [self.currentPage transitionToNextPage];
self.pageNumber = self.currentPage.pageNumber;
}
And finally, the method loadNextPage called from the PageController is defined in the view controller as:
- (void)loadNextPage{
[self.currentPage loadPage];
}
The loadPage is now called from the next page, because it was already set before in self.currentPage = [self.currentPage transitionToNextPage];.
Here I lose the reference to the previous page, but it's not yet dealloc'ed because it still has a timer running, so I'm not having any issues doing this.
In the long term, and for future reusability, maybe the code is a bit confusing, but this way I can call the methods in the correct order, only when the timer finishes.
Another way that I was doing before was using notifications. I was listening to a notification called "#loadNextPage" which was posted by the current page controller when the timer was done. That way, I wasn't calling 3 or 4 additional methods in order to load the next page, but was only calling one.
I think the main advantage I have when using notifications is that the code is much simpler, and I just need to post/listen to a notification and call a single method.
I already thought about using a delegate, but I don't think it can be applied here the way I'm doing the transitions between pages.
I constantly run tests and measure the performance about memory and processor usage, and so far it's doing great. I don't have any memory leaks, and the app runs smoothly both on an iPad 2 with iOS 8 (non-retina) and iPad 4 with iOS 7 (retina).
EDIT:
I'm going to call the next page controller from inside the animation block or timer, without passing any information to the view controller.
I was assuming that it would retain the pages and cause leaks or have bad memory management performance, but that's not true. :)
Timers and similar hacks are simply wrong here. So is blocking or polling as a way of waiting. This is the standard asynchronous pattern. Look at any of the many Cocoa asynchronous methods to see how to deal with it, such as presentViewController:animated:completion:. Instead of writing a method transitionToNextPage, you write a method transitionToNextPageWithCompletion:. It takes a block parameter. When everything is over, the method calls the block, thus calling back into your code.
I have a UILabel and a checkbox button. When checkbox button is checked, display the uilabel,else display the label for 5 seconds and hide the label.When I go to other view and come back to this view,my app gets crashed saying "[_UILabelLayer isHidden]: message sent to deallocated instance "
NSString *display = [NSString stringWithFormat:#"Scanned!\n\nFormat: %#\n\nContents:\n%#", formatString, result.text];
if(checkbox==NO){
[self hideLabel:display];
}
//method to hide the label for 5 seconds
- (void)hideLabel:(NSString*)text{
self.decodedLabel.hidden=NO;
[self performSelector:#selector(hideLabel) withObject:nil afterDelay:5];//3sec
}
-(void)hideLabel{
self.decodedLabel.hidden= YES; //app crashed at this point
}
Could anyone tell me why this is crashing?
[self performSelector:#selector(hideLabel) withObject:nil afterDelay:5];
is causing issues. According to my understanding, you are popping the view controller and then coming back to it. when the controller is popped, all of the associated view are cleared and deallocated. Now in your case, this happens within the span of 5 sec. So when the timer hits, it cannot find that label(which is already deallocated).
So either you can use nstimer and invalidate it as soon as the screen goes away and hide/unhide the label when the next time screen appears depending upon the check mark state.
Please let me know if this helps.
I'm having some really weird behavior in my UIViewController. I'm connecting two devices via the Multipeer Framework and once a iPhone starts sending data I want the other iPhone to display a UIViewController which handles the received data. So once iPhone A starts sending, iPhone B posts a NSNotification. The notification is arriving pretty fast and the code below is being executed:
- (void)presentPeerView:(NSNotification *)notif
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ClientViewController *clientView = (ClientViewController *)[storyboard instantiateViewControllerWithIdentifier:#"peerView"];
[clientView setImage:_image];
clientView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self.window.rootViewController presentViewController:clientView animated:YES completion:nil];
}
Now here comes the weird part. First of all, the ClientViewController gets displayed. But there is no animation, it just appears out of nowhere. My viewDidLoad gets called and runs following code:
- (void)viewDidLoad
{
[self.view setBackgroundColor:[UIColor colorWithPatternImage:[self blurImage:_image];
[self.imageView setImage:_image];
}
Now, I see the blurred image, which is fine. But the (non blurred) image dosen't displays in the UIImageView. I mean the image get's passed properly, otherwise I couldn't see the blurred Background.
I also have a few Interface elements from the Storyboard:
So the UIViewController loads with the blurred background Image:
As you can see, none of the Storyboard UI Elements are loaded, except for the UISwitch (which looks quite strange, too).
After a couple of seconds (it depends, sometimes up to 30, sometimes only 3 seconds) the whole UI loads up:
So, maybe someone of you had the same issue before, I really don't understand why the UI does take so much time to "load". The CPU percentage is somewhere around 3%.
I also added a NSLog() to the this completion block:
[self.window.rootViewController presentViewController:clientView animated:YES completion:^{
NSLog(#"done.");
}];
which is called instantly. The viewDidLoad seems to work fine, there are no lines it might get stuck, if I do a NSLog at the end of viewDidLoad it gets called instantly as well, but the view (as you can see in the images below) isn't loaded (completely).
From the MCSession class reference:
Important: Delegate calls occur on a private operation queue. If your app needs to perform an action on a particular run loop or operation queue, its delegate method should explicitly dispatch or schedule that work.
You're seeing this delay because you're doing UIView work on the wrong thread.
There's a number of ways to fix this, for example:
- (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID {
dispatch_async(dispatch_get_main_queue(), ^{
// post your notification or display your view
});
}