Objective-C - C style string allocation leak? - objective-c

I have a very simple app. From the main screen I launch a new view ("learn view") that displays an image and a label. on the learn view there is a menu button and a next button. When the user taps the next button, the view updates the image and the label from char * array of C strings. However, when I do this, the instruments allocations shows a forever growing number of allocations that are not reduced when the view is destroyed by clicking the menu button. If I just display the learn view then click menu there is no problem, the allocations go up and then go back down to the prior level, but if I click next updating the label.text, then allocations are made that are not recovered. Instruments reports no leaks. here are relevant code snippets:
LearnVC.h
#interface LearnVC : UIViewController {
IBOutlet UIImageView *imageView;
IBOutlet UILabel *labelView1;
NSInteger page;
}
#property (retain, nonatomic) UIImageView *imageView;
#property (retain, nonatomic) UILabel *labelView1;
#property NSInteger page;
- (IBAction)handleNext;
- (IBAction) gotoMenu;
#end
LearnVC.m
#import ...
char *states[] { "Alabama", "Alaska", "Arizona", ... };
#define maxStates 50
#implementation LearnVC
#synthesize ...
- (void)viewDidLoad
{
NSString *tstring;
self.page = 0;
//test!!!!
tstring = [[NSString alloc] initWithCString:states[self.page] encoding:NSUTF8StringEncoding];
labelView1.text = tstring;
[tstring release];
[super viewDidLoad];
}
- (IBAction) handleNext {
NSString *tstring;
self.page++;
if (self.page > maxStates-1) {
self.page = 0;
}
//test!!!!
tstring = [[NSString alloc] initWithCString:states[self.page] encoding:NSUTF8StringEncoding];
labelView1.text = tstring;
[tstring release];
}
- (void)dealloc
{
[imageView release];
[labelView1 release];
[super dealloc];
}
This seems to occur anytime I update a view without removing (deallocating) it and re-adding it. Is there something with old C arrays that don't copy/release properly. Or some issue with UILabel.text properties that don't release memory? Any help is appreciated.
Thanks beforehand, Neal
I converted it to use NSArray -
I added this to the .h file
NSArray *statesArray;
and
#property (retain, nonatomic) NSArray *statesArray;
then in the .m file
in viewDidLoad
NSArray *objects = [NSArray arrayWithObjects:#"Alabama", #"Montgomery",
#"Alaska", #"Juneau" ,..., nil];
self.statesArray = objects;
//I assume that since I didn't init it, I don't need to release objects.
labelView1.text = [self.statesArray objectAtIndex:self.page];in dealloc
[statesArray release];
Then in my handleNext
labelView1.text = [self.statesArray objectAtIndex:self.page];
I run through the entire array, exit, reload, ect. and the allocations climb the first time through but stop climbing after I've been through the list once. This was not the case with the char * array, there must be something else going on, oh well, I'll just have to shed my old C ways and stick to NS/UI classes.
This seems to have solved it. Now I'll work on the image loads and see if the it works the same.

The initWithCString:encoding: method may make an internal copy of the C string you pass in, but is not guaranteed to free the copy during deallocation. Since the C strings you're passing are constant, and therefore can never be freed, you can instead use initWithBytesNoCopy:length:encoding:freeWhenDone: to avoid creating the extra copies. For example:
NSString *s = [[NSString alloc] initWithBytesNoCopy:someCString
length:strlen(someCString)
encoding:NSUTF8StringEncoding
freeWhenDone:NO];

Also, the system does a lot of caching, it acts like each control may cache the text it has used. I found that if I ran through the series a couple of times, then then allocations stopped rising, as if it had cached all it was going to. Allocations did not go down to the starting point when exiting learn view, but subsequent runs did not add anymore.

Related

Possible Memory Leak in Objective-C?

Just starting out with Objective-C after spending years in Python.. still trying to wrap my head around some concepts.. I can't seem to figure this out but every time I either increment up or deduct from myCount it retains the older variable in memory. I am using ARC so shouldn't it autorelease? I have a feeling it has to do with self.varOfMyCount = [NSString stringWithFormat:#"%d", myCount];
Header:
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
IBOutlet NSMenu *statusMenu;
NSStatusItem *statusItem;
}
- (IBAction)itemOne:(id)sender;
- (IBAction)itemTwo:(id)sender;
- (IBAction)itemThree:(id)sender;
#property (assign) IBOutlet NSWindow *window;
#property (nonatomic, copy) NSString *varOfMyCount;
#end
int myCount = 0;
Implementation:
#import "AppDelegate.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength ];
[statusItem setMenu:statusMenu];
[statusItem setTitle:#"Status"];
[statusItem setHighlightMode:YES];
}
- (IBAction)itemOne:(id)sender {
myCount++;
self.varOfMyCount = [NSString stringWithFormat:#"%d", myCount];
NSLog(#"%#",self.varOfMyCount);
[statusItem setTitle:self.varOfMyCount];
}
- (IBAction)itemTwo:(id)sender {
myCount = myCount-1;
self.varOfMyCount = [NSString stringWithFormat:#"%d", myCount];
NSLog(#"%#",self.varOfMyCount);
[statusItem setTitle:self.varOfMyCount];
}
- (IBAction)itemThree:(id)sender {
NSLog(#"Quit.");
[[NSApplication sharedApplication] terminate:nil];
}
#end
From your images, it doesn't really look like there is a problem. In order for your app to run it does need to use memory. Depending on what you do it will use different amounts.
Using [NSString stringWithFormat:#"%d", myCount]; requires more than you might think because you are asking the system to parse your format string and inject parameters into it. Parsing and scanning a string isn't a trivial operation.
In a number of cases when memory is allocated for a task it isn't released. This is usually the case when it's expensive to create (like the scanning structure, or parts of it) or is likely to be used repeatedly.
You should be worried if the memory grows bigger each time you do the same activity and return to your 'transient' state. Consider running multiple iterations of your button push and, between each push, take a heap shot. If each heap shot (apart from the first and last) are empty (or very close to it) then everything's good. If not, it will show exactly what isn't being released.

How to initialize the NSArray in ARC?

I tried initializing the array :
In .h file
#property (nonatomic, retain) NSArray *accounts;
In .m file :
#synthesize accounts;
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *arrList = [acAccountStore accountsWithAccountType:accountType];
// This returns array
self.accounts = [NSArray arrayWithArray:arrList]; // I tried debug after
// this and it gives me data in debugger.
// Note array List have 3 data in it.
}
Now On button click i call a method:
- (IBAction) ButtonClicked :(id) sender {
NSLog(#" data : %#",[self.accounts objectAtIndex:0]); // Breaks at this point.
// When i tried with debug it gives me (no Objective-C description available)
}
Is the initialization of array correct Or If the code is not right please let me know.
Main concern is when i do debug in viewDidLoad, the self.accounts show me proper values. But after doing the click event its empty and throws EXEC_BAD_ACCESS error.
Thanks for help in advance
hm looks fine. A couple of questions then:
Where are you calling the self.accounts = [NSArray arrayWithArray:arrList];
I assume that the array is being setup before your button is being pressed?
There's no real reason that arc should be clearing out the variable. Have you set a strong reference to it or a weak one? If you're using self. on a variable, you should have :
#property (nonatomic, strong) NSArray *accounts;
or similar to that in the .h file and then
#synthesize accounts;
in the .m file.
If you've got weak instead of strong then ARC may possibly clear the memory but it still shouldn't.
Update:
Create a property for your account store as well. I had this exact issue recently and this fixed it.
#property (nonatomic, strong) ACAccountStore *accountStore;
Original Answer
Because you're using ARC, you need to change your property declaration from
#property (nonatomic, retain) NSArray *accounts;
to:
#property (nonatomic, strong) NSArray *accounts;
With the latest LLVM compiler, you don't need to synthesize properties either. So you can remove #synthesize accounts.
You should always use defensive coding as well, so in your - buttonClicked: method, you should do:
- (IBAction)buttonClicked:(id)sender {
if (self.accounts) {
NSLog(#"data: %#", [self.accounts objectAtIndex:0]);
}
}
This makes sure that the pointer to the array is valid.
You can also check to make sure an item in an array exists before trying to read it by doing:
- (IBAction)buttonClicked:(id)sender {
if (self.accounts.count > 0)
NSLog(#"data: %#", [self.accounts objectAtIndex:0]);
}
}

NSMutableArray as instance variable alway null

After many hours wasted, I officially turn to the experts for help!
My problem lies with using a NSMutableArray as an instance variable, and trying to both add objects and return the array in a method in my class. I am obviously doing something fundamentally wrong and would be grateful for help...I have already tried all the suggestions from other similar questions on stackoverflow, read apples documentation, and basically all combinations of trial and error coding I can think of. The mutable array just alway returns (null). I've even tried creating properties for them, but still the array returns (null) and then I also am running into memory management problems due to the retain while setting the property, and the init in the init method for the class.
Here is what I am trying to do:
1) Loop through a series of UISwitches and if they are 'switched on', add a string to the NSMutableArray
2) Assign this mutable array to another array in another method
Any help much appreciated,
Andy
And for some code...
fruitsViewController.h
#import <UIKit/UIKit.h>
#interface fruitsViewController : UIViewController
{
NSMutableArray *fruitsArr;
UISwitch *appleSwitch;
UISwitch *orangeSwitch;
}
#property (nonatomic,retain) NSMutableArray *fruitsArr; // ADDED ON EDIT
#property (nonatomic,retain) IBOutlet UISwitch *appleSwitch;
#property (nonatomic,retain) IBOutlet UISwitch *orangeSwitch;
- (IBAction)submitButtonPressed:(id)sender;
#end
fruitsViewController.m
#import "fruitsViewController.h"
#implementation fruitsViewController
#synthesize fruitsArr; // ADDED ON EDIT
#synthesize appleSwitch, orangeSwitch;
/* COMMENTED OUT ON EDIT
-(id)init
{
if (self = [super init]) {
// Allocate memory and initialize the fruits mutable array
fruitsArr = [[NSMutableArray alloc] init];
}
return self;
}
*/
// VIEW DID LOAD ADDED ON EDIT
- (void)viewDidLoad
{
self.fruitsArr = [[NSMutableArray alloc] init];
}
- (void)viewDidUnload
{
self.fruitsArr = nil;
self.appleSwitch = nil;
self.orangeSwitch = nil;
}
- (void)dealloc
{
[fruitsArr release];
[appleSwitch release];
[orangeSwitch release];
[super dealloc];
}
- (IBAction)submitButtonPressed:(id)sender
{
if ([self.appleSwitch isOn]) {
[self.fruitsArr addObject:#"Apple"; // 'self.' ADDED ON EDIT
}
if ([self.orangeSwitch isOn]) {
[self.fruitsArr addObject:#"Orange"; // 'self.' ADDED ON EDIT
}
NSLog(#"%#",self.fruitsArr); // Why is this returning (null) even if the switches are on?!
[fruitsArr addObject:#"Hello World";
NSLog(#"%#",self.fruitsArr); // Even trying to add an object outside the if statement returns (null)
}
#end
It seems like your init function is never called. If you're initializing this view controller from a NIB, you need to use initWithCoder. If not, just declare your fruitsArr in viewDidLoad.
Use view did load instead of init...
- (void)viewDidLoad
{
fruitsArr = [[NSMutableArray alloc] init];
}
Change that init for viewDidLoad and see what happens
Is your init method ever being called (in complicationsViewController). Add a NSLog to check this, you might be calling initWithNib: maybe.
At viewDidUnload you should remove self.fruitsArr = nil;, or, if you want to keep it, then initialize the fruitsArr in viewDidLoad (and remove it from init).
because fruitsArr don't be init.
you should do this first:
fruitsArr = [[NSMutableArray alloc] init];
so, I think you don't run - (id)init before you use fruitsArr.

NSArray will not initialize in my MVC view

The first few programs I wrote in ObjC worked but were a cluttered mess so this time I wanted to do it right and employ MVC. All of the pieces work and have been tested and things were going great until I tried to copy an NSMutableArray from the model through the VC to the view. The exact same format and code was used and works fine in another aspect of the program but this particular view uses drawRect and breaks if I don't retain the array. When I do it causes a leak. To isolate the problem and create a workaround I ended up loading the array directly from the pList. It looks like this:
#interface HWView : UIView <UIGestureRecognizerDelegate>
{
NSMutableArray *drawStates;
}
#property (nonatomic, retain) NSMutableArray *drawStates;
in the .m
#implementation HWView
#synthesize drawStates;
- (void)awakeFromNib
{
[self HWVReset];
}
-(void)HWVReset
{
NSLog(#"HWVReset:");
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *plistPath = [documentsDirectorystringByAppendingPathComponent: #"PLIST_drawState.plist"];
self.drawStates = [[NSMutableArray arrayWithContentsOfFile:plistPath]retain];
NSLog(#"drawStates:%#",self.drawStates);
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
//draw code
}
- (void)dealloc
{
[self.drawStates release];
[super dealloc];
}
So this runs but it leaks. I remove the retain from: "self.drawStates = [[NSMutableArray arrayWithContentsOfFile:plistPath]retain];" and it crashes. Any help is appreciated.
Of course : [NSArray array] is an autoreleased object. So you'll be calling a deallocated object. Read about Autorelease in Apple's docs.

NSMutableDictionary not retaining values

I am semi-new to Objective-c and confused with why my NSMutableDictionary is not retaining information. I am declaring my variable in the header file:
#interface view_searchResults : UIViewController <UITableViewDataSource, UITableViewDelegate> {
NSMutableDictionary *imageDicationary;
}
#property (nonatomic, retain) NSMutableDictionary *imageDictionary;
Then in my .m file, I have the following:
#synthesize imageDictionary;
-(UIImage *)getImageForURL:(NSURL*)url {
UIImage*image;
image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
[imageDictionary setObject:image forKey:#"test"];
if([imageDictionary objectForKey:#"test"]){
NSLog(#"Exists");
}
}
There is obviously other code to support this, but I can confirm that a URL is being passed, and the file is downloading correctly elsewhere. Also, I can confirm that this function is being executed, and I am not referring to the NSMutableDictionary anywhere else in the document.
Thanks!
Where do you create your NSMutable dictionary? If this really is all the code you have you need to create the dictionary:
#implementation view_searchResults
- (id) init;{
self = [super init];
if(self) {
imageDicationary = [NSMutableDictionary alloc] init]; // should also be released in dealloc.
}
return self;
}
If this is the error then the reason you are not causing a crash is because in objective-C it is valid to send a message to the nil object - it just does nothing.
You havent told us whether the "Exists" NSLog is executed, you also are NOT returning the image.
In other words, I fail to see your problem
Has imageDictionary been initialized? (alloc/init?)