Will Ullrich
Will Ullrich

Reputation: 2228

Strange ARC Memory Release Issue

I have an application under development and I am struggling to understand why Xcode appears to be telling me it is holding on to (what seems) almost everything created, causing some memory pressure every so often..

So far I am presenting all of my views, information, etc. fairly simply. I have 2 ViewController's, one that appears upon launch, and the other once a login has been deemed successful.

The rest of the information that is presented is handled with various subclasses that I have created (most of which are subclasses of UIView, UILabel, or UIButton).

One class that I use very frequently is called MenuView, which is quite literally a class that creates...a menu.

- (MenuView *)createMenuWithFrame:<FRAME> size:<SIZE> type:<TYPE> view:<VIEW>{
    MenuView *menu = [MenuView alloc] initWithFrame:<FRAME>];
    /// Code for creating a UIView with a background image for a menu.
    ...
    /// Done. Now the menu gets added to the passed view <VIEW>. 
    return menu;
}

My issue seems to be persistent throughout each view that I present, as practically right now each view that is added to the hierarchy (shown) is a MenuView, or uses a menu.

EXAMPLE

I am presenting views quite simply, with calls to initialize a subclasses.

Let's take my settings view subclass SettingsView for an example.

HomeViewController.m, like stated above, view controller #2.

#pragma mark Settings
- (IBAction)openSettings:(id)sender {
    [SettingsView createSettingsViewForView:self.view];
}

SettingsView.m. NOTE: In my MenuView.m class I add the created menu to whichever UIView I pass to it.

#import "SettingsView.h"

UIView *screenView;

@implementation SettingsView  // a UIView subclass type in interface (.h)

#pragma mark - Create View
+ (void)createSettingsViewForView:(UIView *)view {
    // Here I create a MenuView. This method is just a wrapper of 
    // the menu class. It doesn't create a SettingsView, but rather 
    // creates another MenuView *menu.

    // The menu is given some content, then "shown" (alpha 0 -> 1) 
    // through the MenuView class by using [menu animateMenu:menu];
}

I understand that using images frequently will be "expensive" in terms of memory allocation, but I am quite certain it should still be released (removed from used memory) if handled correctly. In my case, I thought I was handling it correctly, and still think so..

Upon closing ANY MenuView item, I animate the menu out of view (alpha), then upon completing the animation, I remove the menu from the superview.

- (void)closeMenuView:(UIButton *)closeButton {
    MenuView *menu = (MenuView *)closeButton.superview;
    [UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction animations:^{
        <CUSTOM TRANSITION OUT OF VIEW>
    } completion:^(BOOL finished) {
        [menu removeFromSuperview];
    }];
}

PROBLEM

When I present the settings view from the HomeViewController.m, my memory (which is already higher than I would like) spikes ~10 MB. I understand that optimizing the image sizes depending on the screen size / res will greatly reduce some of the memory usage, but that doesn't matter with the issue I am having.

Once the ~10 MB is added, from let's just say 88 MB to 98 MB, closing the menu has no effect whatsoever in the eyes of Xcode.

Using Instruments, I find opening the menu, then closing the menu WILL in fact lower Persistent Memory used, which is also what it should be (~11 MB + 1.5 MB when settings opened.

The other disturbing issue which makes me doubt my methods of "removing" the views, is that when I present and remove a larger (more complex) menu, one is called MailView, the memory will climb each time the view is "opened", but never releases any memory, so it continues to grow. Instruments tells me there is no memory leak, but clearly there is an issue of retaining this memory.

Here is a screenshot of a recording through instruments. This is traced from `Details -> Statistics -> Allocation Summary (Search for "Mail") -> MailView.

enter image description here

It seems for whatever reason my app continues to create MailView's without destroying them.


QUESTION

My question is (thanks for staying with me), is this in a bug with Xcode and/or Instruments in the reading of my memory usage? Or is this an internal issue with application. I know there is probably more I need to provide to get the best answer, but from what I have shown, and explained, each view is simply a UIView that gets added to the view hierarchy, then it is removed (subsequently "killing" any subviews chained in view underneath).

There are many posts here on SO about issues similar to this, but many suggest what I am doing (removeFromSuperview, = nil (which I tried), use Instruments, manually dealloc, though I am using ARC).

If anyone as an idea of something I am missing, or simply am unaware of, I would greatly appreciate you letting me know. Thanks

FYI

I understand that the iOS devices seem to basically hold on to everything it can, unless it A) thinks you don't need it anymore (most likely from timing out) or B) there is memory pressure, so it gets rid of the "least important" items. But again, this seems to not be the case, as I receive memory warnings on the occasion from these issues I am having.

UPDATE:

(I have just updated to 8.2.1 today so I was able to see the backtrace (with Malloc enabled in the scheme) now through the DMG in Xcode rather than having to go through Instruments)

Here is a snip of the structure when selecting one of the MailView items that it is holding. This is after I open and close Mail 2 times, hence the 2 views it is keeping. This is what I need to fix.

enter image description here

Here is a snip of the graph of the memory of another run having opened and closed the mail view many times.

enter image description here

Upvotes: 1

Views: 227

Answers (1)

Will Ullrich
Will Ullrich

Reputation: 2228

Well.. it seems that despite my best efforts of destroying any of the custom "views", that would most certainly be quite ineffective in destroying objects that are retaining these "views" that continue to be cloned and duplicated across the applications VM while running.

In particular, the culprit was no other than NSArray. For each view that was being created, a simple array, CUCells *cells of the views subviews (cells) was generated and stored. I had initially designed the subclass to be a strong reference to it's parent, but needed to change it to become independently created for each time it was "opened". Having made this change, cells was being initialized and allocated each time the view was opened, failing to be destroyed along with [cell removeFromSuperview].

  • A simple override of this method along with cells = nil and so forth with the other "strong" refs allowed for the application to correctly destroy all ties to the previously destroyed view.

Upvotes: 1

Related Questions