Does this code leak? - objective-c

I just ran my app through the Leaks in Instruments and I am being told that the following code causes leaks, but I don't see how.
I allocate some NSMutableArrays in my viewDidLoad with this code:
- (void)viewDidLoad {
[super viewDidLoad];
self.currentCars = [[NSMutableArray alloc] init];
self.expiredCars = [[NSMutableArray alloc] init];
}
Then I populate these arrays inside of my viewWillAppear method with the following:
[self.currentCars removeAllObjects];
[self.expiredCars removeAllObjects];
for (Car *car in [self.dealership cars]) {
if ([car isCurrent])
[self.currentCars addObject:car];
if ([car isExpired])
[self.expiredCars addObject:car];
}
And later in the code I release these arrays here:
- (void) viewWillDisappear:(BOOL)animated {
if (currentCars != nil) {
[currentCars release], currentCars = nil;
}
if (expiredCars != nil) {
[expiredCars release], expiredCars = nil;
}
[super viewWillDisappear:animated];
}
Any ideas? Thanks!

Your leak is here:
self.currentCars = [[NSMutableArray alloc] init];
self.expiredCars = [[NSMutableArray alloc] init];
Assuming that you declared property accessores like this:
#property(nonatomic, retain) NSMutableArray *currentCars;
#property(nonatomic, retain) NSMutableArray *expiredCars;
In my opinion, the best way to find leaks (other than using Instruments) is to keep track of the retain count manually.
If you were to do that with for example currentCars, you would find your leak easily. Here is what happens:
self.currentCars = [[NSMutableArray alloc] init];
// The 'init' makes the retain count 1.
// 'self.currentCars = ..' translates to the setCurrentCars: method.
// You probably did not implement that method yourself,
// but by synthesizing your property it is automatically implemented like this:
- (void)setCurrentCars:(NSMutableArray *)array {
[array retain]; // Makes the retain count 2
[currentCars release];
currentCars = array;
}
// In your viewWillDisappear: method
[currentCars release], currentCars = nil; // Makes the retain count 1 so the object is leaked.
The solution is simple. Use this:
NSMutableArray *tempMutableArray = [[NSMutableArray alloc] init];
self.currentCars = tempMutableArray;
[tempMutableArray release];
A little sidenote. You shouldn't release your objects in viewWillDisappear:. The recommended place to do that is dealloc. So your code would be:
- (void)dealloc {
[currentCars release], currentCars = nil;
[expiredCars release], expiredCars = nil;
[super dealloc];
}

The problem is (probably) that you are using the property accessors for the initial setting of the arrays in -viewDidLoad. Since well-implemented property accessors will retain the object, you are getting 1 retain from the +alloc and another retain from assigning it. To fix this, you should release your arrays after assigning them or use [NSMutableArray array] to get an autoreleased one to use for your initial assignments.

Unless you're doing something very odd in currentCars, expiredCars, dealership or cars, no, there's no leak there.
Instruments' pointer to the location of a leak isn't necessarily where the object is actually leaked, per se. If I were to guess, I'd say you're probably neglecting to release either currentCars or expiredCars in your dealloc method.

Related

Objective-C Array of Objects

Although experienced with OOP, I am an absolute newbie with Objective-C. I have the following code:
// header files have been imported before this statement...
CCSprite *treeObstacle;
NSMutableArray *treeObstacles;
#implementation HelloWorldLayer {
}
-(id) init
{
// create and initialize our seeker sprite, and add it to this layer
treeObstacles = [NSMutableArray arrayWithObjects: nil];
for (int i=0; i<5; i++) {
treeObstacle = [CCSprite spriteWithFile: #"Icon.png"];
treeObstacle.position = ccp( 450-i*20, 100+i*20 );
[self addChild:treeObstacle];
[treeObstacles addObject: treeObstacle];
}
NSLog (#"Number of elements in array = %i", [treeObstacles count]);
return self;
}
- (void) mymethod:(int)i {
NSLog (#"Number of elements in array = %i", [treeObstacles count]);
}
#end
The first NSLog() statement returns "Number of elements in array = 5". The problem is that (although treeObstacles is a file-scope variable) when calling the method "mymethod", I'll get an EXC_BAD_ACCESS exception.
Can anybody please help me?
Thanks a lot
Christian
you created treeObstacles by
treeObstacles = [NSMutableArray arrayWithObjects: nil];
which will return an autoreleased object, and you didn't retain it so it will be released soon
you have to retain it by calling retain on it
[treeObstacles retain];
of simple create it by
treeObstacles = [[NSMutableArray alloc] init];
and you need to remember to release it when done like
- (void)dealloc {
[treeObstacles release];
[super dealloc];
}
you need to read more about management in Objective-C
https://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/MemoryManagement.html
or use ARC so no need to worry retain/release anymore
http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
another problem, you need to call [super init] in your init method
- (id)init {
self = [super init];
if (self) {
// your initialize code
}
}
otherwise your object won't initialize properly

My NSMutableArray loses its objects outside of function scope even after alloc / init

I'm totally stumped on this one. I have an NSMutableArray which is declared in my header and set as a property, synthesized etc. I then call a function that allocates and initializes the array, and I add custom objects to it. I do a for each loop after the objects are added to ensure that they are actually contained within the array and they are. Once the program goes outside of this function scope, though, suddenly the array is empty.
header file:
#interface ScheduleViewController : UITableViewController {
NSString *login_id;
NSMutableArray *events;
}
- (id)initWithID:(NSString*)l_id;
- (void)grabURLInBackground; // ASIHTTP example method
#property (nonatomic, retain) NSString *login_id;
#property (nonatomic, retain) NSMutableArray *events;
#end
implementation:
#synthesize events;
- (void)requestFinished:(ASIHTTPRequest *)request
{
// Use when fetching text data
NSString *response = [request responseString];
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSArray *eventDics = [parser objectWithString:response error:nil]; // an array of dictionaries of events
NSDateFormatter *dateForm = [[NSDateFormatter alloc] init];
// Allocate empty event object and initialize the mutable array
Event* event = [[Event alloc] init];
self.events = [[NSMutableArray alloc] initWithCapacity:[eventDics count]];
// loop through the array of dictionaries
for (int i = 0; i < [eventDics count]; i++)
{
NSDictionary *dict = [eventDics objectAtIndex:i];
for(NSString *key in dict) {
// for the sake of readability i wont include the code
// but the event is populated here
}
[self.events addObject:event];
[event release];
}
NSLog(#"Array Count: %i", [self.events count]);
for (Event *e in events) {
NSLog(#"eventid: %i, type: %#, price: %f, name: %#", e.event_id, e.type, e.price, e.name);
}
[parser release];
[dateForm release];
}
So the above code works fine and prints out the variables from the Event objects that are stored in the events mutable array.
What I want to do is use the events array in another function now, and when I try to, the count is 0 and also no objects are stored in the array when I look at it.
In viewDidUnload I set self.events = nil; and in dealloc I do [self.events release]
You are doing your alloc/init for the Event *event object outside of your for loop. This means you are adding the same object every time you add it to the array. You should move this line:
Event* event = [[Event alloc] init];
To the inside of your
for (int i=0 ... loop.
Not sure that would explain the symptoms you are seeing, but it could, since the following statement:
[event release]
is also releasing that one allocated object once for every time through the loop - so you are releasing the object multiple times. If you move the Event alloc to the inside of the loop then the release will be ok. (adding the object to the array will retain it so its ok to release it, but you need to allocate a new Event each time through the loop).
Basically your code should look like this: (note I've also added an autorelease to your array alloc).
// Allocate empty event object and initialize the mutable array
self.events = [[[NSMutableArray alloc] initWithCapacity:[eventDics count]] autorelease]; // assigning to the retain property will retain it, so autorelease it or it will be retained twice. Could also have used the arrayWithCapacity convenience method here instead and then wouldn't need to autorelease.
// loop through the array of dictionaries
for (int i = 0; i < [eventDics count]; i++)
{
Event* event = [[Event alloc] init]; // Allocate a new Event each time through the loop so you are adding a unique object to the array each time.
NSDictionary *dict = [eventDics objectAtIndex:i];
for(NSString *key in dict) {
// for the sake of readability i wont include the code
// but the event is populated here
}
[self.events addObject:event];
[event release];
}
I see many problems with this code. These include the fact that you are releasing objects at inappropriate times, and that you are getting confused about the scope of different objects. It seems to me that one of the biggest problems that you are having is re-allocating your events array every time the requestFinished: method is called. In your init method, you should do something like this:
- (id)init {
if ((self = [super init])) {
// Since it is a retain property, we should autorelease it when
// assigning to it, thus preventing an extra retain.
self.events = [[[NSMutableArray alloc] initWithCapacity:[eventDics count]] autorelease];
}
}
With that being said, here is how I would rewrite your requestFinished: method, as well as your dealloc method:
- (void)requestFinished:(ASIHTTPRequest *)request {
NSString *response = [request responseString];
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSArray *eventDics = [parser objectWithString:response error:nil]; // an array of dictionaries of events
NSDateFormatter *dateForm = [[NSDateFormatter alloc] init];
// Clear the already allocated events array
[self.events removeAllObjects];
for (int i = 0; i < [eventDics count]; i++) {
// note how I assign event in here
Event *event = [[Event alloc] init];
NSDictionary *dict = [eventDics objectAtIndex:i];
for (NSString *key in dict) {
// Do whatever it is you do here
}
[self.events addObject:event];
[event release];
}
NSLog(#"Array Count: %i", [self.events count]);
for (Event *e in events) {
NSLog(#"eventid: %i, type: %#, price: %f, name: %#", e.event_id, e.type, e.price, e.name);
}
[parser release];
[dateForm release];
}
Finally, you can simply set the events property to nil in the dealloc method:
- (void)dealloc {
self.events = nil;
[super dealloc];
}
The only reason that I can think of for the array being empty is that a) it's contents are being deallocated, or b) it itself is being deallocated and set to nil. The pieces of your code that I fixed could possibly cause both of these. Try the changes that I have made, and see if they make a difference.
So I figured out the problem and it's due to an error on my part. After stepping through the function calls more closely, it turns out that the table view delegate method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
was being called before populating the array with requestHasFinished. I was calling requestHasFinished indirectly through viewDidLoad but I think that the table view delegate method was being called when the view controller is initialized. Init was being called before viewDidLoad because the view controller is actually handled within a tab view controller which initializes all of the view controllers for each tab at the time when itself is initialized. That's another matter to get into.. I'm not sure if I like everything being initialized and setup before the views are even displayed but.. something to research.
Anyways thanks again for the help.

Using self.objectname causes profiler to report a memory leak

Please help;
Header File
#import <Foundation/Foundation.h>
#interface MyClass : NSObject {
NSMutableString * myString;
}
#property (nonatomic, retain) NSMutableString * myString;
-(id) init;
-(void) dealloc;
#end
Implementation File
#import "MyClass.h"
#implementation MyClass
#synthesize myString;
-(id) init {
if ((self = [super init])) {
self.myString = [[NSMutableString alloc] init];
}
return self;
}
-(void) dealloc {
[super dealloc];
[self.myString release];
}
#end
Usage
MyClass * m = [[MyClass alloc] init];
[m release];
//-- Xcode 4 profiler reports a memory leak here.
However, when the code in implementation file of the class is changed to not use the [self.myString .....] notation, then no memory leak is reported.
So,
-(id) init {
if ((self = [super init])) {
myString = [[NSMutableString alloc] init];
}
return self;
}
}
and
-(void) dealloc {
[super dealloc];
[myString release];
}
works fine. No memory leaks reported.
Any ideas - is it profiler or is it me (be nice)?
Your memory leak is not caused by using your setter. Your memory leak is caused by you not managing your memory correctly!
If you declare the following property
#property (nonatomic, retain) id value;
That means that the compiler generates methods that look something like this (highly simplified):
- (id)value {
return value;
}
- (void)setValue:(id)aValue {
[value autorelease];
value = [aValue retain];
}
When you use dot-notation, self.value = obj is desugared into [self setValue:obj]. Thence, you are actually causing obj to be retained within the setter. If you initially create an owning reference to obj (by using an +alloc without a corresponding -release or -autorelease), you'll have over-retained obj, and it will never be deallocated. Hence, you need to do something like this:
id obj = [[[NSObject alloc] init] autorelease];
self.value = obj;
or
id obj = [[NSObject alloc] init];
self.value = [obj autorelease];
or
id obj = [[NSObject alloc] init];
self.value = obj;
[obj release];
Whatever you do, you need to make sure that when you assert ownership of an object (by retaining it), you also release it.
Setter methods in Objective-C equate to a reatain of the new object and release of the old object. In your case the compiler will generate a setter method for your myString property that looks something like...
- (void)setMyString:(NSMutableString*)aString {
[myString autorelease];
myString = [aString retain];
}
When you invoke self.myString = in your init method this translates to a call to the setter. The setter in turn retains the object you pass to it. Because you've directly alloc'd the NSString it begins life with a retain count of one, you then call the setter and the retain count becomes two.
There's two approaches to fixing the problem, the first would be to add a call to [myString autorelease] after you alloc it. Or secondly switch your init method to directly assign the ivar...
// in your init method...
myString = [[NSMutableString alloc] init];
It's a good idea to avoid setter usage in init methods, not because of retain counts but because the object as a whole is not yet fully initialized.
#property (nonatomic, RETAIN)
you are retaining my friend. You have to release the object twice then because the retain count is 2
here is what you should do in the INIT method:
NSString *str = [[NSString alloc] initWithString:#"Hello World!"];
self.myString = str;
[str release]; // dont leak
Also I do not recommend using self.someProperty in the class itself. Doing so requires 1 extra objc_msgSend() to be done to access your variable and will slow down your application.

Why does my array stay empty?

Hi i'm a objC noob. I have a problem filling an NSMutableArray with objects.
for(id p in tmpArray){
Person *person = [[Person alloc] init];
person.usrName = p;
[persons addObject:person]; // after this line the persons
// array is still empty
[person release];
}
Persons is a property NSMutableArray and the problem is that it's empty. Is it the release of the person object too early or have I instanciated it wrong?
You need to initialize your array in the -init method, like this:
NSMutableArray *array = [[NSMutableArray alloc] init];
self.persons = array; // will be automatically retained
// because you're using the property
[array release]; // we alloced - we release it
Don't forget to release it:
-(void)dealloc {
self.persons = nil; // previous value of property 'persons' will be released
[super dealloc];
}
Make sure you've alloced and initialised the array before you try to add things to it

Initializing an instance variable

With an instance variable myArray:
#interface AppController : NSObject
{
NSArray *myArray;
}
Sometimes I see myArray initialized like this:
- (void)init
{
[super init];
self.myArray = [[NSArray alloc] init];
return self;
}
and sometimes I see it with a more complicated method:
- (void)init
{
[super init];
NSArray *myTempArray = [[NSArray alloc] init];
self.myArray = myTempArray
[myTempArray release];
return self;
}
I know that there's no difference in the end result, but why do people bother to do the longer version?
My feeling is that the longer version is better if the instance variable is set up with a #property and #synthesize (possibly because the variable has already been alloced). Is this part of the reason?
Thanks.
If myArray is a property and it's set to retain or copy (as it should be for a property like this), then you'll end up double-retaining the variable when you do this:
self.myArray = [[NSArray alloc] init];
The alloc call sets the reference count to 1, and the property assignment will retain or copy it. (For an immutable object, a copy is most often just a call to retain; there's no need to copy an object that can't change its value) So after the assignment, the object has retain count 2, even though you're only holding one reference to it. This will leak memory.
I would expect to see either a direct assignment to the instance variable
myArray = [[NSArray alloc] init];
Or proper handling of the retain count:
NSArray *newArray = [[NSArray alloc] init];
self.myArray = newArray;
[newArray release];
Or the use of autoreleased objects:
self.myArray = [[[NSArray alloc] init] autorelease]; // Will be released once later
self.myArray = [NSArray array]; // Convenience constructors return autoreleased objects
This is an idiom used in mutators (sometimes called "setters"), but I think you typed it slightly wrong. Usually it looks like this:
-(void)setMyName:(NSString *)newName
{
[newName retain];
[myName release];
myName = newName;
}
The new name is retained, since this instance will need to keep it around; the old name is released; and finally the instance variable is assigned to point to the new name.
I have a feeling you mean this:
NSArray* a = [NSArray arrayWithObjects:#"foo", #"bar", nil];
and this
NSArray* a = [[NSArray alloc] initWithObjects:#"foo", #"bar", nil];
//...
[a release];
With the first style, the static method performs an alloc/init/autorelease on it for you so you don't have to. With the second style, you have more control over when the memory is released instead of automatically releasing when you exit the current block.
That code will crash your application. The second version only copies the pointer then releases the instance. You need to call [object retain] before releasing the reference.