I'm doing some memory profiling on instruments and I feel like I have code that seems to be doing proper memory management. However, instruments is convinced that I'm leaking and I can't figure out how to make the leak go away.
In my Event.h I have.
#property (nonatomic, copy) NSString *organizer;
#property (nonatomic, copy) NSString *type;
#property (nonatomic, retain) NSDate *startTime;
#property (nonatomic, retain) NSDate *endTime;
#property (nonatomic, copy) NSString *coverCharge;
#property (nonatomic, copy) NSString *ageLimit;
#property (nonatomic, copy) NSString *dressCode;
#property (nonatomic, copy) NSString *venueName;
#property BOOL attendingFlag;
They're all released in dealloc
- (void) dealloc {
[type release];
[organizer release];
[startTime release];
[endTime release];
[coverCharge release];
[ageLimit release];
[dressCode release];
[venueName release];
[super dealloc];
}
And in my factory class, I have
-(Event*) getEvent:rs {
Event *event = [[Event alloc] init];
event.objId = [NSNumber numberWithInt:[rs intForColumn:DATABASE_EVENT_ID_FIELD]];
event.name= [rs stringForColumn:DATABASE_EVENT_NAME_FIELD];
event.organizer = [rs stringForColumn:DATABASE_EVENT_ORGANIZER_FIELD];
event.type = [rs stringForColumn:DATABASE_EVENT_TYPE_FIELD];
event.desc= [rs stringForColumn:DATABASE_EVENT_DESCRIPTION_FIELD];
event.venueName = [rs stringForColumn:DATABASE_EVENT_VENUE_NAME_FIELD];
event.coverCharge= [rs stringForColumn:DATABASE_EVENT_COVER_CHARGE_FIELD];
event.dressCode = [rs stringForColumn:DATABASE_EVENT_DRESS_CODE_FIELD];
event.ageLimit = [rs stringForColumn:DATABASE_EVENT_AGE_LIMIT_FIELD];
event.region = [[[Region alloc] initWithIdAndName:[NSNumber numberWithInt:[rs intForColumn:DATABASE_EVENT_REGION_ID_FIELD]] name:[rs stringForColumn:DATABASE_EVENT_REGION_NAME_FIELD]] autorelease];
event.community = [[[Community alloc] initWithIdAndName:[NSNumber numberWithInt:[rs intForColumn:DATABASE_EVENT_COMMUNITY_ID_FIELD]] name:[rs stringForColumn:DATABASE_EVENT_COMMUNITY_NAME_FIELD]] autorelease];
event.address = [rs stringForColumn:DATABASE_EVENT_ADDRESS_FIELD];
event.address2 = [rs stringForColumn:DATABASE_EVENT_ADDRESS2_FIELD];
event.city = [rs stringForColumn:DATABASE_EVENT_CITY_FIELD];
event.state = [rs stringForColumn:DATABASE_EVENT_STATE_FIELD];
event.zip = [rs stringForColumn:DATABASE_EVENT_ZIP_FIELD];
event.country = [rs stringForColumn:DATABASE_EVENT_COUNTRY_FIELD];
event.phone = [rs stringForColumn:DATABASE_EVENT_PHONE_FIELD];
event.webpage = [rs stringForColumn:DATABASE_EVENT_WEBPAGE_FIELD];
return [event autorelease];
}
You may notice that I'm setting more attributes on the event than I have mentioned above and that's because I have Event extending another Entity which is more generic. The reason I didn't even post that code is because according to instruments, I'm leaking on even the setters for the Event class itself.
Instruments complains that there is a leak on the Event alloc and another one on every line in the getEvent selector. The rs is a resultset object from a library (FMDB) that I'm using all over the app and this seems to be the only entity where these leaks are detected so I'm pretty sure that's not the issue. I've actually used this same library for another project recently, and there were no leaks caused by it so I've eliminated it as the source of the leak.
(a) I'm clearly autoreleasing the event object when I return it.
(b) All my setters are getting autoreleased objects so I'm only increasing the retain count as recommended by the memory management docs for objective c.
Any idea why the alloc line and almost every line following it may be leaking?
The answer is that code elsewhere is retaining your Event object. Leaks can only show you where memory that has been leaked was created, Leaks cannot show you the code that should have been written to correctly release the object after creation!
The other lines are all marked as leaks because the Event object is leaking.
The thing to do is to add the Allocations instrument in addition to Leaks, and make sure it's set to "record reference counts" (little (i) in the Allocations bar on the time graph). Then start your app, observe the leak. Then select the Allocations tool, select "created and still living", and look up Event objects still around.
Then click on the arrow next to the address, and you'll get a list of every retain and release for that object. You can usually figure out from that what retained the object that should have also released it later but did not.
Related
Currently attempting to save an array that is populated according to which cells in a UITableView are chosen and saving this array in an instance of a seperate object. I am getting the array to populate just fine, however, my save method, which is an IBAction that is invoked by clicking on a Bar Button doesn't seem to be working. Here is some code:
-(IBAction)saveWorkout:(id)sender {
Workouts *new = [[Workouts alloc] init];
[new addNewWorkout:customWorkout];
[customWorkout removeAllObjects];
}
This code is from the first class.
And here is the code for my addNewWorkouts method in the Workouts class:
-(void)addNewWorkout:(NSMutableArray*)array {
NSMutableArray *temp = [[NSMutableArray alloc] init];
temp = array;
self.workoutList = temp;
[temp release];
}
Here is my "Workout.h"
#import <Foundation/Foundation.h>
#interface Workouts : NSObject {
NSString *workoutName;
NSMutableArray *workoutList;
NSString *description;
int *reps;
int *weights;
int *sets;
}
#property (nonatomic, retain) NSString *workoutName;
#property (nonatomic, retain ) NSString *description;
#property (nonatomic, retain) NSMutableArray *workoutList;
-(void)addNewWorkout:(NSMutableArray*)array;
#end
Before running this code, I get a Warning from Xcode saying that 'Workouts may not respond to 'addNewWorkouts.'
Anyone know what is causing this error? Once I build & run, I click on the Save button and the app crashes with a unrecognized selector sent to instance 0x3b04410 error.
You call [new addNewWorkouts:customWorkout]
when the method's selector is addNewWorkout: (note that there is no plural in the method name)
This will make a bad method call and result in a crash.
Also, there is a problem with the memory management of the addNewWorkout method.
1- NSMutableArray *temp = [[NSMutableArray alloc] init];
2- temp = array;
3- self.workoutList = temp;
4- [temp release];
You allocate a new NSMutableArray on line 1, then lose its reference on line 2 when you replace its pointer by 'array'. The allocation you just made is lost and the program will leak.
Then, on line 4, you send a release message to 'temp' which actually points to 'array', resulting in the release of the parameter that you received and not the temporary object.
Is there a reason whny you create a temporary array? You can just assign the property and make the property copy or retain it, depending on your needs.
Which way is correct?
NSString *c = [[NSString alloc] init];
self.character = c;
[c release];
or
self.character = [[NSString alloc] init];
And why? Thanks.
It depends on how your property is declared. If you used
#property (nonatomic, retain) NSString *someString;
The setter will be created to retain someString, in which case the correct way is your first method. If you used:
#property (nonatomic, assign) NSString *someString;
Your second method will be correct, since it will just assign the pointer and not retain anything.
It depends on how you've defined your property.
If it's copy or retain, the synthesized setter (setCharacter: in your example) will take ownership of any objects you assign to the property. In this situation your first example is correct. The second would lead to a memory leak as you've claimed ownership of the NSString twice and you will (probably) only relinquish ownership once; thus the memory can never be reclaimed.
If it's assign on the other hand, the setter won't do anything special and your second example would be correct. The first would result in an EXC_BAD_ACCESS error if you tried to do anything with the NSString. I should note that you generally only use assign for primitive types and delegates.
I suggest you have a read over the Memory Management Programming Guide and the Declared Properties section of The Objective-C Programming Language guide.
The answer depends on your #property definition. Likely it's something like (retain) or (copy) for an NSString. In that case, assigning to self.character will increment the retain count. So the bottom:
self.character = [[NSString alloc] init];
You've set the retain count to 1 with the alloc and self.character will also retain it for a count of 2, so that'll leak. Should be
self.character = [[[NSString alloc] init] autorelease];
or the top version.
The answer to this now with iOS 5 is to use ARC.
This is the code. It is pretty straight forward. I made two classes one is returning the error and hydrate the iVar of another class (TheView) and show it to the User. however I cant figure it out why the View return Null at all time. Thanks is advance guys.
#interface AccountControllerModel : NSObject {
NSString *anError;
}
#property (nonatomic, retain) NSString *anError;
AccountControllerModel.m
#synthesize anError;
- (void)uploadFailed:(ASIHTTPRequest *)theRequest{
RegistrationViewController *regoVC = [[RegistrationViewController alloc] init];
[regoVC manageTheError:#"THIS IS AN ERROR"];
[regoVC release]; regoVC = nil;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#interface RegistrationViewController : UIViewController {
NSMutableString *test;
}
#property (nonatomic, assign) NSMutableString *test;
#synthesize test;
-(void)viewDidLoad {
test = [[NSMutableString alloc] init];
}
-(void)manageTheError:(NSString *)theError{
self.test = [NSMutableString stringWithFormat:#"%#",theError];
resultOfRegistration.text = [NSString stringWithFormat:#"%#",self.test];
NSLog(#"test is %#",self.resultOfRegistration.text); //It comes back Null
}
Alex is right, some clarification on what's not working would help but by looking through I may have found your error. [[NSNotificationCenter defaultCenter] postNotificationName:#"Blah" object:self], you have object set to nil which could be your issue with the notification.
There are a number of problems with your code.
#property (nonatomic, assign) NSMutableString *test;
Two things here, one, exposing a NSMutable* object in a property is never a good idea, two you should 'copy' value objects, especially because this is how you're treating it in your code. Make this #property (nonatomic, copy) NSString *test;
regoVC.test = [NSMutableString stringWithString:self.anError];
You're assigning an autoreleased object to an assign property, this is a leak, the change above will fix that.
NSLog(#"test is %#",test); // It is perfect as you expect
test isn't in scope here, but I'd assume that was supposed to be regoVC.test, these other changes should remedy the situation.
I've spent a couple of days trying to find out what's going on. I have read loads of Memory Management documentation and I am sick to death of hearing "for every alloc you need a release" - I know that and I still can't figure out why my code is producing memory leaks.
I am writing a simple custom class with an NSMutableDictionary as one of its properties. Basically it mimics an XMLELement. I cannot for the life of me figure out why the allocation of a dictionary is causing a memory leak. The leak occurs on the device as well as the simulator - 5 leaks on the device, and 20 on the simulator.
The leak occurs when I declare and allocate the variable *tmp.
There is also a leak when I set the attribute details (name and value).
This is driving me nuts. Please help!
Part of the code:
#interface IMXMLElement : NSObject {
NSString *strElementName;
NSString *strElementValue;
NSMutableDictionary *dictAttributes;
}
#property (nonatomic, retain) NSString *strElementName;
#property (nonatomic, retain) NSString *strElementValue;
#property (nonatomic, retain) NSMutableDictionary *dictAttributes;
#end
#implementation IMXMLElement
#synthesize strElementName;
#synthesize strElementValue;
#synthesize dictAttributes;
-(id)initWithName:(NSString *)pstrName
{
self = [super init];
if (self != nil)
{
self.strElementName = pstrName;
**LEAK NSMutableDictionary *tmp = [[NSMutableDictionary alloc] init];
self.dictAttributes = tmp;
[tmp release];
}
return self;
}
-(void)setAttributeWithName:(NSString *)pstrAttributeName
andValue:(NSString *)pstrAttributeValue
{
**LEAK [self.dictAttributes setObject:pstrAttributeValue forKey:pstrAttributeName];
}
-(void)dealloc
{
[strElementName release];
[strElementValue release];
[dictAttributes release];
[super dealloc];
}
The access this class using the following code:
NSString *strValue = [[NSString alloc] initWithFormat:#"Test Value"];
IMXMLElement *xmlElement = [[IMXMLElement alloc] initWithName:#"Test_Element"];
[xmlElement setAttributeWithName:#"id" andValue:strValue];
When you have strings as properties, declare them as copy, not retain.
NSMutableDictionary *tmp = [[NSMutableDictionary alloc] init];
self.dictAttributes = tmp;
[tmp release];
the above is unnecessary, instead do:
(retain count will automatically be incremented for this autorelease object)
self.dictAttributes = [NSMutableDictionary dictionaryWithCapacity:0];
in dealloc do:
(retain count will automatically be decremented)
self.dictAttributes = nil;
normally for properties you just set them to nil instead of explicitly releasing them
since the get/setter handles that for you.
Try [dictAttributes removeAllObjects] before releasing dictAttributes.
Edit:
Also, you will positive allocation because you are allocating memory for "tmp". The memory will be retained because you now have a reference from dictAttributes.
You then have more positive allocation when you add elements to the dictionary, which also need to be allocated and are kept in memory by the dictionary's internal references
Typical syntax is NSMutableDictionary *tmp = [[[NSMutableDictionary alloc] init] autorelease];
I have a table view that when loading creates a person object
Person.h
#import <UIKit/UIKit.h>
#import "TwitterHelper.h"
#interface Person : NSObject {
NSDictionary *userInfo;
NSURL *image;
NSString *userName;
NSString *displayName;
NSArray *updates;
}
/*
#property (retain) NSString *userName;
#property (retain) NSString *displayName;
#property (retain) NSDictionary *userInfo;
*/
#property (nonatomic, copy) NSURL *image;
#property (retain) NSArray *updates;
- (id)initWithUserName:userName;
#end
Person.m
#import "Person.h"
#implementation Person
/*
#synthesize userName;
#synthesize displayName;
#synthesize userInfo;
*/
#synthesize image;
#synthesize updates;
- (id)initWithUserName:(NSString *)user{
userName = user;
userInfo = [TwitterHelper fetchInfoForUsername:user];
displayName = [userInfo valueForKey:#"name"];
image = [NSURL URLWithString:[userInfo valueForKey:#"profile_image_url"]];
updates = [TwitterHelper fetchTimelineForUsername:userName];
return self;
}
- (void)dealloc
{
/*
[userName release];
[displayName release];
[updates release];
[userInfo release];
[image release];
*/
[super dealloc];
}
#end
Inside my UITableView method cellAtRowForIndexPath I am creating each person object and assigning the image property like so...
Person *person = [[Person alloc] initWithUserName:userName];
NSData *data = [[NSData alloc] initWithContentsOfURL:person.image];
[data release];
When I run this in Instruments it highlights the NSData *data... row saying that is where the leak is.
Why is it leaking there?
First, you need to understand the difference between instance variables and properties and getter/setters.
instance variables (ivars) are variables stored in
your object. You access an ivar from within a method simply by naming it (eg "userName").
properties define an
interface to your object, allowing
information to be read and/or written
to your object.
getters/setters implement that interface and may use an ivar as backing storage
You access a property by using a getter/setter, either explicitly (eg [self userName]) or (equivalently) using dot syntax self.userName. Note that these two notations are exactly identical. You declare a property (ie, you declare an interface to your object) using #property in the interface of your object, something like:
#property (copy) NSString* userName;
This declartion is essentially equivalent to typing:
- (NSString*) userName;
- (void) setUserName: (NSString*) theUserName;
You implement a property, either by using #synthesize (which simply tells the compiler to write the getter/setter for you) or by implementing it yourself (ie, you write methods implementation for userName and setUserName). There is also a rarely used third option, #dynamic, which tells the compiler you will handle the methods at run time, essentially just silincing the warning you would otherwise get.
Next, you need to read and understand the memory management rules. Its only 9 short paragraphs, go read it now, I'll wait. Done? good.
Further, you need to know that you should not use getters/setters in either the init or dealloc routines.
So your init routine should look something like this:
- (id)initWithUserName:(NSString *)user{
userName = [user copy];
userInfo = [[TwitterHelper fetchInfoForUsername:user] retain];
displayName = [[userInfo valueForKey:#"name"] copy];
image = [[NSURL URLWithString:[userInfo valueForKey:#"profile_image_url"]] copy];
updates = [[TwitterHelper fetchTimelineForUsername:userName] retain];
return self;
}
Note that you take ownership of each value you store in an ivar with retain or copy. Generally, you use copy for NSString to convert an NSMutableStrings into NSStrings you own, rather than retain which would leave you holding a reference to a possibly mutable string. The same issue applies to NSArray/NSDictionary, but we will assume TwitterHelper intends to hand off the fetched data.
Your dealloc will have to release the various ivars:
- (void)dealloc
{
[userName release];
[displayName release];
[updates release];
[userInfo release];
[image release];
[super dealloc];
}
Anywhere else in your code you would use self.userName to access or change the properties, rather than access the ivars directly.
Note that you might consider not storing the displayName (and similarly image) at all, but simply implement a property getter that retrieves it from userInfo. To do this, delete the displayName ivar, change the property to:
#property (readonly) NSString *displayName;
remove the #synthesize displayName, and add a manual getter:
- (NSString*) displayName
{
return [userInfo valueForKey:#"name"];
}
and remove the release in dealloc.
Note that you do not need to retain/release the value in displayName - you return a value that the receiver does not own and it is up to them to copy/retain it if they want to keep it.
If you choose to create a property, you should use:
self.image = [NSURL URLWithString:[userInfo valueForKey:#"profile_image_url"]];
in your init message and not
image = [NSURL URLWithString:[userInfo valueForKey:#"profile_image_url"]];
Setting the value without the self prefix will not call the copy or retain message, and will create a memory problem (not necessarily a leak).
This might be what Instruments is pointing you to.
(This obviously applies to all properties!)
Alternatively, if you don't want to use the accessor, then retain or copy the value retrieved, e.g.:
image = [[NSURL URLWithString:[userInfo valueForKey:#"profile_image_url"]] retain];
You are calling alloc on Person but not releasing it. You've leaked your person object.
(in your cell configuration)