- (void)fadeOutSplash {
UIImageView *splash = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Default-Landscape~ipad.png"]];
[self.window.rootViewController.view addSubview:splash]; // <-- OBJECT IS BEING RETAINED HERE
[UIView animateWithDuration:0.5
animations:^{
splash.alpha = 0;
}
completion:^(BOOL finished) {
[splash removeFromSuperview];
}];
}
I think ARC is retaining my "splash" when I add it to the subview of the rootViewController. ARC should release "splash" when I run my animation completion because it removes my "splash" from it's own super view. However, I can see in the allocation instruments that this parent view controller is staying allocated and it shows the problem line being where splash is added to the rootViewController. What can I do to make sure "splash" is released?
I fixed this problem, but I'm not exactly sure how.. Here's the likely solution:
- (void)removeFromSuperView
{
// Use this space to manually release any non IB pointers / variables as needed
self.someDictionaryIMadeInInit = nil;
while(self.subviews.count > 0) [[self.subviews objectAtIndex:0] removeFromSuperView];
[super removeFromSuperView];
}
This a little trick I came up with for ARC related views. I recommend it more as a last resort because truly this should be solved the appropriate way, but it's worth a try to save you from tearing out your hair!
Related
I am adding an animated UIImageView (an explosion) to a view but it seems to not be retained. Here is the implementation of the Explosion class, a subclass of UIImageView:
#implementation Explosion
#define EXPLOSION_DIMENSION 20
- (instancetype)initAtPoint:(CGPoint)point {
if (self = [super initWithFrame:CGRectMake(0, 0, EXPLOSION_DIMENSION, EXPLOSION_DIMENSION)]) {
self.center = point;
self.image = [UIImage imageNamed:#"explosion.png"];
}
return self;
}
- (void)boom {
self.alpha = 0;
[UIView animateWithDuration:0.5
delay:1.0
options: UIViewAnimationOptionCurveLinear
animations:^{
self.alpha = 1;
}completion:^(BOOL finished){
NSLog(#"here");
// [self removeFromSuperview];
}];
}
#end
When hitting a breakpoint on NSLog(#"here"), the debugger shows no self. Huh? Any reference to self in the completion code causes a crash so I can't do a [self removeFromSuperview] there.
Here is the code in the view where it is instantiated:
Explosion * explosion = [[Explosion alloc] initAtPoint:p];
[_explosionImageViews addObject:explosion];
[self addSubview:explosion];
[explosion boom];
NSLog(#"subviews: %d array: %d", self.subviews.count, _explosionImageViews.count);
The view contains 8 other subviews. The output looks like this:
subviews: 9 array: 1
subviews: 10 array: 2
subviews: 9 array: 3
Note that the number of subviews resets to 9. Nowhere do I remove the explosion from its superview, it just seems to magically go away (garbage collection?). Sometimes the subviews.count gets up to 23 before it magically resets to 9.
I put in the _explosionImageViews array just to retain the objects so they would not go out of scope even though it seems that being added to the subviews of the view should already be doing this.
The animation is supposed to stay around for 0.5 seconds but stays much longer. Then several of them all disappear at the same time. Again, garbage collection?
Any ideas what is going on?
You're right--your explosion object is not being retained.
To you retain it, use a strong property:
#property (strong, nonatomic) Explosion *explosion;
Then, instantiate it in your implementation, like so:
self.explosion = [[Explosion alloc] initAtPoint:p];
Of course, if your explosionImageViews are being retained, then follow that property, to see if it is being released somewhere.
I hope that helps!
I added about 500 views to my viewController.view.
This action took about 5 seconds on target.
Now I want the screen to refresh after each subview I'm adding, so the user will see them appears one by one on screen.
I tried this in my viewController:
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
for(int i=0; i<500; i++)
{
//...Create aView
[self.view addsubview:aView];
[self.view setNeedsDisplay];
}
}
I run it and nothing happened for 5 seconds then all views appeared at once.
I made sure that [self.view setNeedsDisplay] called from the main thread context.
Any idea how to make those subviews appear one by one?
I found a simple solution. I added a new property to my viewController - 'subviewsCount' witch was initialised to 500. then called the following method from viewDidLoad:
-(void) addSubviewsToMotherView
{
self.subviewsCount -=1;
if (self.subviewsCount >= 0)
{
[UIView animateWithDuration:0.0
delay:0.0
options:UIViewAnimationOptionLayoutSubviews
animations:^{
[self methodToAddSubview];
}
completion:^(BOOL finished){
[self addSubviewsToMotherView];
}
];
}
}
I have a not usual architecture. With one UIViewController and many subview's. I use UIView like viewController and UIViewController for transition animation.
UIView with many subview, which i push on main window, i removeFromSuperview and all ok, but subview not call dealloc method. Also i try to clean objects
- (void) dealloc {
NSLog(#"ActivityShowVC dealloc");
[showView removeFromSuperview];
showView = nil;
}
This called.
- (void) dealloc {
NSLog(#"ActivityShowView dealloc");
[mainScroll removeFromSuperview];
[titleView setDelegate:nil];
[titleView removeFromSuperview];
titleView = nil;
[photoView setDelegate:nil];
[photoView removeFromSuperview];
photoView = nil;
[predictionView setDelegate:nil];
[predictionView removeFromSuperview];
predictionView = nil;
[calendarView setDelegate:nil];
[calendarView removeFromSuperview];
calendarView = nil;
[descriptionView setDelegate:nil];
[descriptionView removeFromSuperview];
descriptionView = nil;
}
But this have some problem.
If dealloc is implemented and not called then there is something retaining the instance. You need to rethink the strong references to it.
Dealloc is not a good place to do anything other than releasing resources since the instance is no longer fully functional nor in a complete state. Doing things like: [showView removeFromSuperview]; are best done explicitly prior to deallocation.
As #Greg stated, most or all of what you are doing in dealloc is not needed under ARC. Subviews are now generally weak so no explicit dealloc is necessary. In fact they may be already gone depending on the order of the teardown of the view hierarchy.
I have solved a similar problem. In my Viewcontroller, I have a NSTimer pointer created, when I put the code below in 'viewWillDisappear', 'dealloc' is called.
if (_timer) {
[_timer invalidate];
_timer = nil;
}
in rootviewcontroller this code is working fine
- (IBAction)gotoAboutGame:(id)sender
{
aboutGame *aboutGameObj = [[aboutGame alloc]init];
[UIView transitionFromView:self.view toView:aboutGameObj.view duration:2 options:UIViewAnimationOptionTransitionCurlUp completion:^(BOOL finished) {
[aboutGameObj release];
}];
[self release];
}
but from aboutgame when i am coming back to rootviewcontroller i am getting zombie attacks
- (IBAction)gotoMain:(id)sender
{
ViewController *viewControllerObj = [[ViewController alloc]init];
[UIView transitionFromView:self.view toView:viewControllerObj.view duration:2 options:UIViewAnimationOptionTransitionCurlUp completion:^(BOOL finished) {
[viewControllerObj release];
}];
[self release];
}
it shows the rootviewcontroller view but as i touch any button zombie attacks.
[viewControllerObj release];
put it after animation block
What class is this code in?
This line looks very suspect to me
[self release];
Why it the object releasing itself? Surely the object that created it should be doing this.
by convention, class names begin with capital letters, not lowercase
your memory management is quite wrong; [self release] is pretty much never correct (there are very esoteric cases where it is). What retain is that supposed to balance?
I am presenting a modal view controller which contains a UIScrollView with a bunch of images. When I dismiss my modal view controller I can see in my Allocations (instruments) that memory assigned to the images is hanging around but not causing leaks.
Causing me some confusion and any help would be great. Here is what I have...
UIViewController - DollViewController: This is my main view controller that I am presenting my modal over. Here is the code -
-(IBAction)openDollCloset {
NSLog(#"Open Closet");
[self playSound:#"soundMenuClick"];
ClosetViewController *closet = [[ClosetViewController alloc] initWithNibName:#"ClosetViewController" bundle:nil];
closet.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self setModalPresentationStyle:UIModalPresentationFullScreen];
[self presentModalViewController: closet animated: YES];
closet.delegate = self;
closet.addToCloset = NO;
[closet setCurrentDoll:myDoll];
[closet openCloset];
[closet release];
[self closeSubmenu];
}
I create my scrollview manually to hold the outfit images.
The object closetScroller is defined with #property (nonatomic, retain) UIScrollView *closetScroller; with a release in dealloc.
- (void)setScrollerValues {
if (closetScroller == nil)
{
self.closetScroller = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 400)];
[self.closetScroller setDecelerationRate:UIScrollViewDecelerationRateFast];
[self.closetScroller setDelegate:self];
[self.closetScroller setPagingEnabled:YES];
[self.closetScroller setShowsHorizontalScrollIndicator:NO];
[self.view addSubview:closetScroller];
}
[self.closetScroller setContentSize:CGSizeMake(outfitCount * 320, 400)];
}
After creating the scrollView I add the images to the scroller. I believe I am releasing everything right. I am creating the images with NSData objects and releasing them.
- (void)addOutfitsToScroller {
// Clear out any old images in case we just deleted an outfit
if ([[closetScroller subviews] count] > 0)
{
CATransition *fade = [CATransition animation];
[fade setDelegate:self];
[fade setType:kCATransitionReveal];
[fade setSubtype:kCATransitionFromRight];
[fade setDuration:0.40f];
[[self.closetScroller subviews] makeObjectsPerformSelector: #selector(removeFromSuperview)];
[[closetScroller layer] addAnimation:fade forKey:#"FadeButtons"];
}
// Set the pageControl to same number of buttons
[pageControl setNumberOfPages:outfitCount];
// Load the outfit image and add it to the scroller
for (int i = 0; i < outfitCount; i++)
{
NSArray *savedDataArray = [[NSArray alloc] initWithArray:[closetItemsArray objectAtIndex:i]];
UIImageView *v = [[UIImageView alloc] initWithFrame:CGRectMake(320*i, 0, 320, 400)];
[v setUserInteractionEnabled:NO];
[v setAlpha:0.40f];
NSData *imageData = [[NSData alloc] initWithContentsOfFile:[savedDataArray objectAtIndex:1]];
UIImage *outfitImage = [[UIImage alloc] initWithData:imageData];
UIImageView *closetImage = [[UIImageView alloc] initWithImage:outfitImage];
[imageData release];
[outfitImage release];
[savedDataArray release];
CGSize frameOffset;
#ifdef LITE_VERSION
frameOffset = CGSizeMake(40, 60);
#else
frameOffset = CGSizeMake(20, 10);
#endif
closetImage.frame = CGRectOffset(closetImage.frame, frameOffset.width , frameOffset.height);
[v addSubview:closetImage];
[closetImage release];
[self.closetScroller addSubview:v];
[v release];
}
}
This all works great. Then when the user selects the "Close" button or selects an outfit I call:
[self.delegate closeClosetWindow];
Which calls the delegate method in the parentViewController which simply makes this call:
[self dismissModalViewControllerAnimated:YES];
I have also tried calling dismissModalViewControllerAnimated:YES from the modal view controller itself. It simply forwards the message to it's parentViewController and closes the modal just the same as the first way. Neither makes a difference with regards to the memory.
So what I do instead is use this method to close the view:
[[self.closetScroller subviews] makeObjectsPerformSelector: #selector(removeFromSuperview)];
[self dismissModalViewControllerAnimated:YES];
When I do that, I can see my Object Allocations decrease in Instruments. Prior to opening the closet view I have object alloc at about 1.85mb. When I open the closet it increases to 2.5 or so depending on how many outfit images are loaded into the UIScrollView. Then when I close the view without the above method it stays at 2.5 and increases the next time I open it. Although when using the above method that removes the outfit images from the scroller, my object alloc drops back down close the the 1.85mb. Maybe a little more which tells me it's hanging on to some other stuff also.
Also note that when looking at the object allocations, I do see the ClosetViewController object created and when I close the modal it's refrence is released. I can see it malloc and free the view as I open and close it. It's not retaining the view object itself. i'm so confused.
Not sure what to do here. I am not getting any leaks and I am releasing everything just fine. Any ideas would be great.
mark
I Noticed object alloc in instruments keeping a live reference to UIScrollView every time I opened modal window. So to fix, when allocating for my scrollview, I instead allocated a new UIScrollView object and assigned it to closetScroller and then released. This took care of the problem
I think you were simply not releasing closetScroller in the dealloc of your view controller.