Search code examples
objective-cnsmutablearraynsuserdefaultsnscodingcustom-object

Storing and Retrieving Custom Object (ARC) Keeps growing memory


hoping someone might be able to shed some light on the issue.

I am retrieving an array of custom objects using NSCoding + NSUserDefaults, and every time I reload the view, the memory continues to grow.

enter image description here enter image description here

Here is the code I use to retrieve the array of custom objects (which is called under ViewDidLoad:

NSData *dataRepresentingSavedArray;

NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
dataRepresentingSavedArray = nil;
dataRepresentingSavedArray = [currentDefaults objectForKey:@"women"];
if (dataRepresentingSavedArray != nil)
{
    NSArray *oldSavedArray = nil;
    oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
    if (oldSavedArray != nil)
        women = [[NSMutableArray alloc] initWithArray:oldSavedArray];
    else
        women = [[NSMutableArray alloc] init];
}

The woman.backgroundimage is what causes the most memory loss, however when I removed that aspect from the custom object and NSCoder completely it still climbed in memory without release, but slower. The ViewController is part of a navigation controller (SWRevealViewController Pod) and when it is selected the memory increases until app crash. Thank you for your time!

EDIT: I figured I should also share how the data is archived. First the image is compressed:

                 CGFloat maxCompressionFactor = 0.1f;
                 CGFloat compressionFactor = 0.9f;
                 int maxImageSize = 60 * 1024;

                 NSData *imageData = UIImageJPEGRepresentation(_theimage, compressionFactor);

                 while ([imageData length] > maxImageSize && compressionFactor > maxCompressionFactor)
                 {
                     compressionFactor = 0;
                     imageData = UIImageJPEGRepresentation(_theimage, compressionFactor);
                 }

                 _theimage = [UIImage imageWithData:imageData];
                 _theimage = [self scaleImage:_theimage toSize:CGSizeMake(320.0,480.0)];

Then all values are stored:

 //store to woman object
                 Woman* woman = [[Woman alloc] initWithFull:nameOfGirl withdate2:date withintervalLength:string  withperiodLength:[NSString stringWithFormat:@"432000"] withpmsLength:[NSString stringWithFormat:@"432000"]initWithbackground:_theimage];

                 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

                 [women addObject:woman];

                 [userDefaults synchronize];

Solution

  • This answer is just a synopsis of the comments.

    The key to understanding what was going on was that some object-- likely the view controller itself, but potentially a custom view object, whatever-- was retaining the objects that you were creating from NSUserDefaults.

    We reduced the number of allocs, and the amount of memory growth reduced, which seemed to confirm that each time ViewDidLoad is hit, an object was created but never deallocated because its retain count never hit zero. Some other object which stayed in memory was keeping a strong reference to it.

    At that point, I simply suggested aggressive nil assignment in the right view lifecycle function (viewDidDisappear?), wrapped in an @autoreleasepool enclosure for good measure. That turns out to solve the problem entirely.

    Assigning nil to the property within the VC ensures that a strong property's data does not stay in memory until the VC itself is deallocated. Using @autoreleasepool tells ARC to deallocate zero-reference-count objects that became zero-reference-count within the enclosure at the termination of the enclosure. I'm thinking of the "pool" as a ledger of reference count transactions within the enclosure. The enclosure keeps its own ARC books and clears its ledger when it's done.