Animations crash App after going into background - iphone

After looking for some feedback on how to clean up animations once they've finished I implemented the following;
- (void)onAnimationComplete:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
UIImageView *AnimationImageEnd = context;
[AnimationImageEnd removeFromSuperview];
[AnimationImageEnd release];
}
and to set them off (I've removed some of the frames and rotation calcs);
UIImageView * AnimationImageStart = [[UIImageView alloc] initWithImage: AnimationImage];
[self addSubview:AnimationImageStart];
[UIView beginAnimations:nil context:AnimationImageStart];
[UIView setAnimationDuration:iSpeed];
AnimationImageStart.transform = transform;
[UIView setAnimationDidStopSelector:#selector(onAnimationComplete:finished:context:)];
[UIView setAnimationDelegate:self];
[UIView commitAnimations];
This seems to be working fine and cleaning up all used memory once the animation completes.
Since implementing this approach I have now got issues with re-opening the App once it goes into the background if an animation had started before hand. The App is now crashing with the following error;
*** -[UIImage _isDecompressing]: message sent to deallocated instance 0x21083ad0
I've had a tinker and it appears to be all down to the release used. If I remove the [AnimationImageEnd release]; the crashes stop (but it obviously starts some memory leaks off..).
Is there something simple I am misunderstanding about the Animations and the App entering / re-opening from the background?
My guess is that iOS is automatically cleaning up any animations in progress once it enters the background and automatically fires the release to each. If this is the case, should I just simply expect them to have been cleaned up and hold a flag within the applicationDidBecomeActive to bypass the release?
Edit 1
I've read that the finished flag on onAnimationComplete will let me know if the animation completed it's full animation or not. Is it enough to just trust that if it did not finish it has already been cleaned up (removed from superview and released)?
Edit 2
As per David's response below I have updated this to a block;
[UIView animateWithDuration:5.0f
delay:0.0f
options:0
animations:^{
imgStart.frame = CGRectMake( 0, 0, 10.0f, 10.0f);
}
completion:^(BOOL finished){
[imgStart removeFromSuperview];
//This still crashes the App as soon as the App returns from being in the background
[imgStart release];
}];
The same behaviour as detailed above is still present. As per Edit 1, is it enough to just assume that whenever the finished BOOL is NO, it has already been cleaned up (removed from superview + released)?
Any information is greatly appreciated.
Ralph

First, please don't use an initial capital letter for an object - reserve those for classes (it makes reading code much harder for most of us).
Generally it's bad practice to release objects in callbacks. Use a dispatch block to the main queue to release the object or a performSelectorOnMainthread:, and see if that helps.
EDIT: didn't mean to imply you needed a complete block. Reading the docs again shows no indication of retain change if the animation does not complete. Since you have a release in your code, you are not using ARC.
Non-ARC: so imgStart is alloc/inited, no autorelease, so its retain count is 1. You add it to the subview, its now two. You (or the system) removes it from the subview, its now 1. The final release makes it 0 (I'm ignoring the retain/release by the block itself). I just cannot see how the system could release it, as its not really anywhere it can be seen (its not an ivar, so viewDidUnload cannot release it). Right now this is a mystery.
Suppose you converted to ARC: imgStart is released at the start of the animation, two things retain it, the subview array and the block. When its removed from the subview array (by whoever), retain count goes down by 1, and when the block completes it will release it as well, causing the retain count to go to 0 so it will be released.
If nothing above rings a bell or helps, you can at least try and find when/where that object is getting dealloced. Create a UIImageView subclass in your .m file:
#implementation MyIV : UIIMageView
#end
#interface MyIV
- (void)dealloc
{
[super dealloc];
NSLog(#"MYIV DEALLOC!!!!");
}
#end
Put a breakpoint on the log message, and you can see exactly where its getting dealloc'd. I've found this so helpful in the past I created an ARC class to do this even for class clusters on github, called Tracker.

Related

scrollViewWillEndDraggin:WithVelocity:targetContentOffset delegate method bug

I am implementing a UIScrollView and its delegate in a UIViewController. It scrolls in the horiztonal way.
WHat I want is to set "magnetism" when the view is dragged and is stabilizing.
to do that, I am listening to the delegate, specially the method mentionned in titled.
It returns me the offset of the final destination.
then I make the scollview display the view which correspond to this destionation with setContentOffset or setvisiblerect methods.
I also NSlog the entire method to catch the bugs.specially right after calling the method I have a nslog which confirmed me/or not, if the method is triggered.
problem : when I build&run, the first nslog is triggered an average of 3 times by dragging.
so the entire effect looks very strange and I can't figure out whether or not it works.
I have found very little things about this on the net. So it would be a great help if you had some clues.
cheers
I am kind of desperate :/
So you want the scroll view to snap to a valid selection when the user is done dragging? You should be able to implement this using the
- (void)scrollViewWillBeginDragging:(UIScrollView *)thisScrollView
and
- (void)scrollViewWillDidScroll:(UIScrollView *)thisScrollView
methods. Without seeing your code I can't tell you exactly how to go about this, but I would recommend keeping track of the thisScrollView.contentOffset.x in a variable, and doing some arithmetic to move the scrollView with a
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
call to move it to the appropriate spot.

UIScrollView's setContentOffset called before new subview is loaded

I have a UIView that I'm sliding in using a UIScrollView. Code looks like this:
[view setObject:object]; // updates a bunch of labels, images, etc
[scrollView addSubview:view];
[scrollView setContentOffset:CGPointMake(320,0) animated:YES];
Problem is, the scrollView doesn't seem to wait for the view to be completely loaded before animating its contentOffset, and so the animation is kinda jerky (almost non-existent on the first slide-in and on older devices). Oddly enough, switching the 3rd line to this fixes it:
[UIView animateWithDuration:0.3 animations:^{
[scrollView setContentOffset:CGPointMake(320,0) animated:NO];
}];
No lag whatsoever, perfectly smooth slide-in. However, this doesn't trigger any of the UIScrollViewDelegate calls, on which I also depend (but which aren't responsible for the lag).
Any idea of how I could tell the UIScrollView to wait for the view to be completely loaded before animating its contentOffset? Or maybe there's something else that I'm missing?
EDIT: before anyone else suggests it: yes, I did try:
[self performSelector:#selector(slideIn) withObject:nil afterDelay:1];
just to see if it would fix it. And yes it does fix the lag, but this is not an actual solution. performSelector:afterDelay: is never a solution, it's only a superficial fix. Plus, you're making the user wait extra seconds every time (since the actual delay may be much shorter than 1 second depending on the device model).
Performing selector after delay 0.01 works because the invocation is scheduled on the runloop vs. calling the method immediately. For that matter, afterDelay:0.0 may also work. Did not try in this particular case but works in may similar situations.
have you tried starting the scrolling in the "viewDidAppear" function of the viewcontroller... maybe this is called when the loading is finished.
otherwise try afterdelay with a very short time like 0.01 .. so that the call is scheduled next after the current work is done.
Try this, set contentSize after some delay (here delay is 2 second).
[view setObject:object];
[scrollView addSubview:view];
[self performSelector:#selector(contentSize) withObject:nil afterDelay:2];
}
-(void) contentSize {
[scrollView setContentOffset:CGPointMake(320,0) animated:YES];
}

UIImageView animation lag

I have a problem with the animation. At start, everything is smooth. However, when the application runs for a certain period of time (around 10 min) then the animation lags.
Here is the code in viewDidLoad:
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(updateImage) userInfo:nil repeats:YES];
And in -(void)updateImage: //image fade in
[aSubview release];
[aSubview removeFromSuperview];
aSubview = [[UIImageView alloc] initWithFrame:CGRectMake(28, 64, 265, 284)];
aSubview.image = [slideShowArray objectAtIndex:randNum];
[aSubview setAlpha:0.0];
[UIImageView beginAnimations:NULL context:nil];
[UIImageView setAnimationDuration:2.0];
[aSubview setAlpha:1.0];
[UIImageView commitAnimations];
[self.view addSubview:aSubview];
UIImageView *film = [[UIImageView alloc] initWithFrame:CGRectMake(20, 34, 280, 344)];
film.image = [UIImage imageNamed:#"film5.png"];
[film setAlpha:1.0];
[self.view addSubview:film];
[film release];
Can anyone explain this? Is it due to memory issue or something else? Thanks in advance.
[self.view addSubview:aSubview];
You are adding a subview in every 3 seconds. So after 10 mins there are 200 subviews which requires too much memory and thus slowing the app. Before adding a new subview, remove the previous ones if they are not needed. And if you need 200 subviews simultaneously then you should reconsider your design.
EDIT: After the edit of the question, why do you need to add a film every time? You are adding a new film object every time the method is called. Note that, super view retains subviews. So if the method is called 20 times then you have 20 film objects in memory.
Yes, your problem is with memory aggregating over time. To solve this problem use Instruments to check memory usage and find out what causes it. You might not release some object. In XCode go to Product -> Profile or press "cmd+i" choose "Allocations" and add "Leaks" manualy. Run Instruments and try to debug your problem.
[self.view addSubview:aSubview];
The above is not the issue at all, from the apple documentation for addSubView .
Views can have only one superview. If
view already has a superview and that
view is not the receiver, this method
removes the previous superview before
making the receiver its new superview.
Animation is always a heavy operation in term of memory that need to store many thing before and after animation, which are not visualable to as application developer. And in your case you are doing it more than 10 min.. in a loop that lead to reduce the availability of memory for next animation operation.

UIView hidden property...is there more to it?

Coming from ActionScript, I would set Sprites to visible=false in order to keep them from being calculated in things like layout, and to ensure they would not respond to events.
In iOS development, I am continuing with this--if a UIView is not needed, I may both animate its alpha to zero and then set hidden=true. I wanted to know if I was wasting my time, or if there is a benefit to this. In my current project, I'm doing so with UIImageViews which are not responding to events anyway.
Is setting hidden to true good practice, or or just additional overhead?
This is the best choice, because setting hidden to true removes view from the render loop. While setting alpha to 0 just makes view transparent.
If you don't need it any more, then you should properly remove it from memory. In that case you would just animate its alpha (to make it look good), then dealloc it.
if you autoreleased it, then all you have to do is remove it from the superview and its retain will hit 0 and be dealloced.
[UIView animateWithDuration:.5
animations: ^ {
[myView setAlpha:0];
}
completion: ^ (BOOL finished) {
[myView removeFromSuperview];
}];

Source of UIView Implicit Animation delay?

I have a block of UIView animation code that looks like this:
[UIView beginAnimations:#"pushView" context:nil];
[UIView setAnimationDelay:0];
[UIView setAnimationDuration:.5];
[UIView setAnimationDelegate:self];
[UIView setAnimationWillStartSelector:#selector(animationWillStart)];
view.frame = CGRectMake(0, 0, 320, 416);
[UIView commitAnimations];
The code basically mimics the animation of a ModalView presentation and is tied to a button on my interface. When the button is pressed, I get a long (.5 sec) delay (on iPod Touch...twice as fast on iPhone 3GS) before the animationWillStart: actually gets called. My app has lots going on besides this, but I've timed various points of my code and the delay definitely occurs at this block. In other words, a timestamp immediately before this code block and a timestamp when animationWillStart: gets called shows a .5 sec difference.
I'm not too experienced with Core Animation and I'm just trying to figure out what the cause of the delay is...Memory use is stable when the animation starts and CoreAnimation FPS seems to be fine in Instruments. The view that gets animated does have upwards of 20 total subviews, but if that were the issue wouldn't it cause choppiness after the animation starts, rather than before? Any ideas?
Try it with a single subview or with no subviews at all to make sure the delay is not caused by so many children.
Profile the code in Instruments to see where exactly the code lags. You might get down to some internal Core Animation function call that will hint you what’s going on.
Try the code without the “lot that’s going on” to make sure you’re not stepping on Core Animation’s toes with your other code.
Or, in short: experiment and measure, because conjectures seldom work when optimizing.
In your pasted block, you specify the selector animationWillStart (no colon), but later in your question, you refer to animationWillStart: (with colon). These selectors are not equivalent, so is it possible that your intended selector is never being called on account of this animation, and is being called 0.5 seconds later on account of some other animation?