views:

1066

answers:

2

I have a view that is added as a subview in a viewcontroller. The subview has delegates methods and the viewcontroller is assinged as its delegate. The subview has a animation and calls a delegate method when the animation is finished.

The problem is that when the viewcontroller is removed from the view by the navigationcontroller the subview isn't deallocated. Probably because it's release count is >0. When the viewcontroller is removed from view before the subview animation finishes the subview tries to call the delegate (which is the viewcontroller which doesn't exist anymore) method and i get a EXC_BAD_ACCESS.

Maybe some sample will clarify things ;):

The view

- (void)somefunction {
  [UIView beginAnimations:nil context:NULL];
  [UIView setAnimationDelegate:self];
  [UIView setAnimationDidStopSelector:@selector(viewDidAnimate:finished:context:)];
  [UIView setAnimationDuration:0.3]; 
  self.frame = CGRectMake(0, 320, 320, 47);
  [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
  [UIView commitAnimations];
}

-(void)viewDidAnimate:(NSString *)animationID finished:(BOOL)finished context:(void *)context{
  if (self.delegate != NULL && [self.delegate respondsToSelector:@selector(viewWasAnimated:)]) {
    [delegate viewWasAnimated:self];
  }
}

viewcontroller

- (void)viewDidLoad{
  [super viewDidLoad];
  MYView *myview = [[MYView alloc] init];
  myview.delegate = self;
  [self.view addSubview:myview]; 
  [myview release];
}

- (void)viewWasAnimated:(MYView *)view{

}

I found out that after the

[UIView commitAnimations];

line the retainCount of the view is 2, and after the

[delegate viewWasAnimated:self];

the release count is 3. So this is probably why the view isn't released. I know I am not supposed to look at retain counts but don't know what else to do.

A: 

First off i guess it's a typo but:

 [self.view addSubview:adview];

should be

 [self.view addSubview:myview];

About your question:

1-Put MYView *myview; in your viewcontroller.h

2-Before you release the viewcontroller, call:

[myview removeFromSuperView];

This should deallocate your subview.(if there are no other additional retains)

Good luck.

ahmet emrah
I added myview to the view of the viewcontroller and then released it. myview is then only retained by the self.view of the viewcontroller. Once the self.view is released all it's subviews are also released.Yes it was a type :).
SW
+1  A: 

Your view is not deallocated because the UIView beginAnimation Block retains it for the whole animation duration. So if your controller can be released during your animation you can remove the UIView from the controller's view in the dealloc and in the animationDidStop: method check if the superview is equal to nil if so, then don't send the message

-(void)viewDidAnimate:(NSString *)animationID finished:(BOOL)finished context:(void *)context{ if (self.superview != nil && self.delegate != NULL && [self.delegate respondsToSelector:@selector(viewWasAnimated:)] ) { [delegate viewWasAnimated:self]; } }

Another approach you could take is to cancel the UIView animation but that is not as intuitive.

In the dealloc method of the UIViewController do

[UIView beginAnimations:nil context:NULL]; [UIView setAnimationBeginsFromCurrentState:YES]; [UIView commitAnimations];

This will cancel the current animations. And you will receive finished == NO in the animationDidStop message (void)viewDidAnimate:(NSString *)animationID finished:(BOOL)finished context:(void *)context{ if ( finished && self.delegate != NULL && [self.delegate respondsToSelector:@selector(viewWasAnimated:)] ) { [delegate viewWasAnimated:self]; } }

Antonio Anchondo
Okay that's what I thought. I added the check to see if the superview is nil. This solved one problem I have. I assume the UIView animation releases my view when its done. Still the [delegate viewWasAnimated:self]; call adds a retain count so the view is never deallocated.
SW
Are you setting the delegate with retain or assign in the @property declaration.. Be sure to declare it as assign because your will create a loop retain if you don't. Since the delegate will be retained by the view and if the view is retained by the delegate then none of the will ever be released.You can easily see who is retaining your view if you can't figure out by overriding the following methods on MyView and setting a break point to see who is making the retain and release calls-(id)retain{return [super retain];}-(void)release{[super release];}Good Luck!
Antonio Anchondo
The delegate was declared as assing. But thanks to the retain release calls I found out I also had a timer retaining the view. I added a [myview cancelTimer] function which invalidates the timer. I call this function in the viewWillDisappear methods of the viewcontroller holding myview. This did the trick. Thanks!
SW