1

I've been developing an app for about a week or so now and I thought it would be a good time to profile it to make sure I was doing everything correctly, what I found was that even though all my objects are getting dealloc'd the allocation amount is going up. When I release an object I do the following:

[object release];
object = nil;

In my app I have an initial view controller that determines to either show my LoginViewController or TimeLineViewController, depending on whether I have an access token. (This part doesn't matter as the problem I am having is within the LoginViewController/SignupViewController.). The login controller has two textfields and two buttons, these buttons either push the sVC onto the navigation view controller or attempt to login.

The weird thing is that the dealloc methods are being called on my views and view controllers but the memory goes up after they are called.

SDK version 7.0 Xcode version 5.0

Edit:

In my LoginViewController this method is called when I get an event from the LoginView that the SignupButton has been clicked:

- (void)signupButtonPressed
{
    SignupViewController *signupVC = [[SignupViewController alloc] init];
    [self.navigationController pushViewController:signupVC animated:true];
    destroy(signupVC);
}

***A note, the destroy macro is as follows:

#define destroy($x)                                             \
if($x)                                                          \
{                                                               \
[$x release];                                               \
$x = nil;                                                   \
}

When the SignupViewController is created the ViewDidLoad method is as follows:

self.view = [[SignupView alloc] initWithFrame:self.view.frame];

    [[(SignupView *)self.view evtSignupButtonPressed] addHandler:AFFHandler(@selector(signupPressed))];
    [((SignupView *)self.view).profileImage addTarget:self action:@selector(profileImagePressed) forControlEvents:UIControlEventTouchUpInside];

[self.navigationController setNavigationBarHidden:false animated:true];

It then creates the UI for the view inside SignupView which looks like this:

- (void)setupUI
{

    self.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:IS_IPHONE5 ? @"genericBackground-568h.jpg" : @"genericBackground.jpg"]];

    _overlayView = [[UIView alloc] initWithFrame:self.frame];

    _scrollView = [[UIScrollView alloc] initWithFrame:self.frame];


    _profileImage = [[UIButton alloc] init];

    profileImageContainer = [[UIView alloc] initWithFrame:CGRectMake(18.5, 0, _profileImage.imageView.image.size.width + 10, _profileImage.imageView.image.size.height + 10)];


    selectProfilePictureText = [[UILabel alloc] initWithFrame:CGRectMake(profileImageContainer.affX, 0, 229, 17)];


    UIView *padding = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 17, 40)];

    _usernameField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 284, 40)];
    _usernameField.delegate = self;

    _passwordField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 284, 40)];
    _passwordField.delegate = self;

    _repeatPasswordField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 284, 40)];

    _repeatPasswordField.delegate = self;

    _emailField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 284, 40)];
    _emailField.delegate = self;

    destroy(padding);

    buttonImage = [[UIImage imageNamed:@"largeButton.png"] copy];
    _submitButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, buttonImage.size.width, buttonImage.size.height)];
    [_submitButton addTarget:self action:@selector(signupButtonPressed) forControlEvents:UIControlEventTouchUpInside];


    destroy(buttonImage);




    [self addSubview:_scrollView];
    [self addSubview:_overlayView];
    [_scrollView addSubview:profileImageContainer];
    [profileImageContainer addSubview:_profileImage];
    [_scrollView addSubview:selectProfilePictureText];

    [_scrollView addSubview:_usernameField];
    [_scrollView addSubview:_passwordField];
    [_scrollView addSubview:_repeatPasswordField];
    [_scrollView addSubview:_emailField];
    [_scrollView addSubview:_submitButton];


    destroy(profileImageContainer);
    destroy(selectProfilePictureText);

}

**A note, Ive omitted all the code that changed properties of those objects like changing the backgroundColour and such.

The dealloc methods of the SignupVC and the SignupView are as follows:

SignupView:

- (void)dealloc
{

    self.usernameField.delegate = nil;
    self.passwordField.delegate = nil;
    self.repeatPasswordField.delegate = nil;
    self.emailField.delegate = nil;

    AFFRemoveAllEvents();

    destroyAndRemove(_usernameField);
    destroyAndRemove(_passwordField);
    destroyAndRemove(_repeatPasswordField);
    destroyAndRemove(_emailField);
    destroyAndRemove(_profileImage);
    destroyAndRemove(_submitButton);
    destroyAndRemove(_scrollView);
    destroyAndRemove(_overlayView);

    if(buttonImage)
        destroy(buttonImage);


    [super dealloc];
}

SignupVC (this gets called after the backbutton of the NavigationBar is pressed)

    - (void)dealloc
    {
        [[(SignupView *)self.view evtSignupButtonPressed] removeHandlersForObserver:self];
        [((SignupView *)self.view).profileImage removeTarget:self action:@selector(profileImagePressed) forControlEvents:UIControlEventTouchUpInside];
        destroy(profileImage);
        destroyAndRemove(self.view);


        [super dealloc];
    }

DestroyAndRemove does this:

#define destroyAndRemove($x)                                    \
if($x)                                                          \
{                                                               \
[$x removeFromSuperview];                                   \
[$x release];                                               \
$x = nil;                                                   \
}
ApperleyA
  • 603
  • 4
  • 11
  • Do the objects contain any references to external resources? Is the memory management scheme based on garbage colleciton, reference counting? Don't happen to know for this environment. – joshp Sep 28 '13 at 23:22
  • 3
    Which instrument are you seeing this in? – RyanR Sep 28 '13 at 23:23
  • Is that Objective-C code? If so, please add an objective-c tag. I presume that `[object release]` is similar to the `free()` function in C. If so, deallocating memory makes it available for further allocation within the program; it doesn't necessarily reduce the size of memory occupied by the program. – Keith Thompson Sep 28 '13 at 23:24
  • I get an "Internal Server Error" trying to follow the link to your repo. – Keith Thompson Sep 28 '13 at 23:25
  • This environment uses reference counting. I am using the Allocations and the Leaks instruments. It is objective-c and I forgot to add that tag but it's added now. The repo link works now. – ApperleyA Sep 28 '13 at 23:30
  • I do understand that it wont reduce the memory footprint but from what I see it actually is raising it and never bringing it down. – ApperleyA Sep 28 '13 at 23:32
  • @KeithThompson `release` and `free` are **not** the same thing. Releasing an object means lowering its reference count. When it gets to 0 the object is deallocated. – Gabriele Petronella Sep 28 '13 at 23:41
  • 1
    Please include a [_short_, self-contained sample](http://sscce.org) that demonstrates the problem rather than a link to your entire codebase. Questions on SO should be self-contained and continue to be useful after you've gotten your answer. Reviewing your entire app is not really what this site is here for. – jscs Sep 28 '13 at 23:41
  • 3
    Is there a reason you've chosen to develop a modern iOS app without using [ARC](http://stackoverflow.com/questions/6385212/how-does-the-new-automatic-reference-counting-mechanism-work)? – jscs Sep 28 '13 at 23:46
  • This is the way I was trained to develop iOS apps and I'm comfortable in continuing to use this method of memory management. – ApperleyA Sep 28 '13 at 23:56
  • 3
    That destroy macro is a bit bizarre. – Hot Licks Sep 29 '13 at 00:03
  • Is it safe to assume that the variables beginning with `_` are the instance variables for properties, and that those properties are defined `retain`, AND that there is a `dealloc` method that cleans up all the properties correctly? – Hot Licks Sep 29 '13 at 00:06
  • The macro just saves me time in writing the release/nil code. I've used it in other projects and never had an issue but I even took it out and wrote out all of my release/nil code and it still happens. If anyone here looking at this would like an instruments dump let me know. – ApperleyA Sep 29 '13 at 00:07
  • 3
    What pray tell does `destroyAndRemove` do? And why do you need such a complex release scheme -- simply doing `[ptrName release]` is sufficient, though you can follow it up with a set to nil if you want -- no need for the `if` test. – Hot Licks Sep 29 '13 at 00:09
  • Yes those are instance variables of properties, the dealloc method of the view handles the clean-up. – ApperleyA Sep 29 '13 at 00:09
  • 2
    @HotLicks, I agree- the `if` test is simply wasteful. @ApperleyA, consider: 1) Object has been `released` yet *wasn't* set to `nil`, `if` statement will `crash` the app or 2) object has been set to `nil`, calling any method on `nil` doesn't do anything. -> `if` statement to check if an object is already released is bad practice. – JRG-Developer Sep 29 '13 at 00:16
  • @JRG-Developer I agree with you on that one, I will be taking that out of the macro, thank you. That still doesn't display what I'm doing wrong as I noted that I took out the macro all together and it still showed the memory issue. – ApperleyA Sep 29 '13 at 00:25

2 Answers2

5

Switch to ARC. Seriously, there isn't a good reason not to use it, and it may likely fix your memory issues even:

1) Apple strongly encourages and has said they use ARC in their updates and new apps.

2) The vast majority of new app submissions and updates use ARC, and it has been shown to be just as efficient as manual reference counting in the vast majority of cases (your app probably isn't an exception to this).

3) ARC simplifies your life as an Objective-C developer. You don't have to litter your code with release, retain, etc everywhere anymore.

4) There's an easy to use conversion tool:

Goto Edit > Refactor > Convert to Objective C ARC

5) Even if you're using third party libraries that haven't switched to ARC yet (most popular ones already have), you can opt-out of using ARC on individual files. See this other SO post on how to do it.

If you're still having issues after switching to ARC- as mentioned, switching to ARC may likely fix your memory issues- come back, and we'll give this another go.

Community
  • 1
  • 1
JRG-Developer
  • 12,454
  • 8
  • 55
  • 81
  • not properly an answer to the problem, but definitely a sensible piece of advice. +1 – Gabriele Petronella Sep 29 '13 at 00:38
  • 1
    If he switches to `ARC`, and it fixes his issues, I'd call it an answer to the problem. :D – JRG-Developer Sep 29 '13 at 00:40
  • it's kind of a shot in the dark, but yes ^^ – Gabriele Petronella Sep 29 '13 at 00:41
  • 1
    I switched to ARC (went through the wizard and it got me to take out a few lines of cleanup and it converted the rest for me). I ran the build and it worked as it did last time, awesome. I then ran it through instruments and I noticed the same behaviour as last time before enabling ARC. If an object has references to it the dealloc method wont be called on that object but it's being called and I can see it has zero references to it as it's not in the allocated list of instruments and when it is the number of Live is 1 and the Transient is 2(I removed the viewcontroller two times before) – ApperleyA Sep 29 '13 at 00:47
  • What would be the next step for myself to help you debug this issue or make it easier for you to see what's going on? – ApperleyA Sep 29 '13 at 00:51
  • What happens when you profile using the `Leaks` instrument? Do you show any leaks? Also, how much memory does it show you to be using? – JRG-Developer Sep 29 '13 at 00:54
  • Also, what happens if you go back and forth from this controller to the previous one / another controller? Does the memory keeping going up or does it stable out? – JRG-Developer Sep 29 '13 at 00:56
  • Leaks yes, right when I launch the app but I don't see the leaks when building against ios6 (which I still have the memory issue). The leaks pertain to CoreGraphics, for example it says this is a leak: `self.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:IS_IPHONE5 ? @"genericBackground-568h.jpg" : @"genericBackground.jpg"]];` But the amount of memory is 1.08mb with one view and and 1.18mb with two but will go up to 4mb+ if I click on a button (one of the buttons brings up an imagePicker) and the memory from that never gets released too. – ApperleyA Sep 29 '13 at 00:57
  • If it doesn't related directly to your code, there probably isn't much you can do it about it. Sometimes, it may actually be a leak in Apple's frameworks... usually they're small... sometimes, the instrument gets it wrong (object is still being used, will be used later, or will/has been released)... In general, I typically just give stuff that doesn't relate directly to what I wrote. – JRG-Developer Sep 29 '13 at 00:59
  • In using `ARC`, the most common leak in *your* code is due to a retain cycle (one controller strongly referencing a child controller and child controller strongly referencing a parent controller for example). In such case, you will see the leak on the instrument, though. (You also have to watch out when you're using Core Foundation / Core Graphics , etc... which doesn't support ARC, but it doesn't look like this is the case here.) – JRG-Developer Sep 29 '13 at 01:02
  • From what I can tell all my code is exactly how it should be, I've checked it a million times but the memory amount that it goes down when popping the VC and it calling all the dealloc methods isnt equal to how much memory it used to show in the view so I'm under the impression it actually didn't release the view. Is there anything wrong with VC code in ios7 sdk? – ApperleyA Sep 29 '13 at 01:02
  • Yeah,the only leak is the one that shows up at the beginning of the app launching, never any other time throughout it's life cycle. – ApperleyA Sep 29 '13 at 01:03
  • 1) How much memory are you using? 2) Does it keep going up significantly if you go back and forth to the controller or relatively stabilize out? (You want to look at `live bytes`-- currently in use--, not `overall`-- all memory allocs, etc during life of app) – JRG-Developer Sep 29 '13 at 01:04
  • It goes up 10/20kb but then down an amount close but never that amount when I leave the view. If I do anything within that view it will bring it up but only ever bring it down 5/10kb. Lets say I type some text in all the inputBoxes it brings the memory up 15kb and when I went to the view it went up 15kb, it will only go down 10kb. Or if I add an image from my gallery it goes up 3mb, it will only go down 10kb. – ApperleyA Sep 29 '13 at 01:09
  • I wouldn't worry about it-- 20 kb even isn't significant, even if the user only went back and forth between this controller, it won't likely ever cause a problem. Regarding images, `UIImage imageNamed:` method does cache images automatically (so it's faster to load them later), but this is released appropriately under memory pressure. – JRG-Developer Sep 29 '13 at 01:14
  • I know I could use contentsOfFile: as it doesn't create a cache but it's still strange that even memory used in animating my scrollview up (I have my input boxes in a scroll view for when the keyboard comes up) and memory for text input isn't released. Yes, 20kb isnt a huge deal and they will only be signing up once but it goes to 3-4mb if they submit a photo and it's never released. – ApperleyA Sep 29 '13 at 01:18
  • Cache images are released, just not immediately. The cache is beneficial in that it helps load times. Plus, `UIImage` takes care of this automatically for you, and everyone uses it, so it really is pretty much bug free. Once again, I wouldn't worry about it. Cheers. :D – JRG-Developer Sep 29 '13 at 01:20
  • 3
    Thank you for taking the time to try and figure this out, I appreciate it. I accepted this answer as it has a good discussion and you made some good points in the comments. Have a good one. – ApperleyA Sep 29 '13 at 01:22
4

What objects are accreting?

I.e. when using the Allocations instrument, turn on "only track live objects" (or the equivalently named feature). Then user your app and look to what objects are sticking around in memory that shouldn't be. Turn on the reference event tracker, too.

More likely than not, it is a retain cycle. Maybe a timer that is strong referencing your object? Or some other similar relationship.

Once you identify the objects sticking around in memory, then you should be click through to the inventory of retains and releases and see what isn't balanced.

Note that you really should use ARC.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • 1
    I've done this but none of the Views or ViewControllers are sticking around in memory. The only thing that might cause this is a delegate to my custom alert box but it's set to a weak(I changed over to ARC for testing purposes) and before was assign and is set to nil when the alert box is dismissed. – ApperleyA Sep 29 '13 at 01:14
  • 1
    What allocations are sticking around, then? Sort by "live memory". There has to be some allocation somewhere. Alternatively, it might be mapped memory related to images, etc. – bbum Sep 29 '13 at 02:02