I've just figured out why some code has just failed to work. I've got something like the following:
#interface Client : NSObject {
Connection *connection;
}
#property (retain) NSMutableDictionary *channels;
Followed by this implementation:
#implementation Client
#synthesize channels;
- (id)init
{
self = [super init];
if (self) {
// do whatever I want
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
}
return self;
}
Any idea why channels has a memory address of 0x0 in the init constructor for this class? I can't figure out for the life of me why this is happening. I'm definitely calling that constructor, and it should be synthesizing the property allowing me to do whatever I want with it.
Unless you explicitly set channels to a value, it will remain nil. Perhaps you meant to do the following in your init method?
if (self) {
// do whatever I want
channels = [[NSMutableDictionary alloc] init];
}
Two more ways to set channels:
[self setChannels:[NSMutableDictionary dictionary]]; //These two lines...
self.channels = [NSMutableDictionary dictionary]; //...are equivalent.
Note that in that case, because we are using your retain accessors, we use the autoreleased +dictionary rather than alloc/init, which would leak if not using ARC.
Of course, channels must also be properly released in -dealloc, if not using ARC:
- (void)dealloc
{
[channels release];
}
It is 0x0 because you are never setting channels to anything. You at least need to do:
channels = [[NSMutableDictionary alloc] init];
and then in dealloc you must remember to:
[channels release];
Related
I'm having a bit of trouble with memory leaks in my objective c code. Could anyone take a look and let me know what they think?
NSStringArray.h
#interface NSStringArray : NSObject {
NSMutableArray *realArray;
}
#property (nonatomic, assign) NSMutableArray *realArray;
-(id)init;
-(void)dealloc;
#end
NSStringArray.m
#import "NSStringArray.h"
#implementation NSStringArray
#synthesize realArray;
-(id)init {
self = [super init];
if ( self != nil ) {
realArray = [[[NSMutableArray alloc] init] retain];
}
return self;
}
-(void)dealloc {
[realArray release];
realArray = nil;
[super dealloc];
}
Factory.m
+(NSStringArray *)getFields:(NSString *)line {
//Divides the lines into input fields using "," as the separator.
//Returns the separate fields from a given line. Strips out quotes & carriage returns.
line = [line stringByReplacingOccurrencesOfString:#"\"" withString:#""];
line = [line stringByReplacingOccurrencesOfString:#"\r" withString:#""];
NSStringArray *fields = [[NSStringArray alloc] init];
for (NSString *field in [line componentsSeparatedByString:#","]) {
[fields.realArray addObject:field];
[field release];
}
return [fields autorelease];
}
The Leaks tool is saying that the leak occurs when fields is allocated, and when I am adding field string to the fields array.
Also, this function is getting called each line of a file that I'm parsing.
Any tips would be helpful.
Thanks!
This line does a double retain:
realArray = [[[NSMutableArray alloc] init] retain];
it is enough
realArray = [[NSMutableArray alloc] init];
In this piece of code, you break the memory management rules.
for (NSString *field in [line componentsSeparatedByString:#","]) {
[fields.realArray addObject:field];
[field release];
}
You do not own the object pointed at by field so you must not release it.
You have overreleased field so the last object to release it (the autorelease pool in your case) is releasing an already dealloc'd object.
From the docs:
An allocation message does other important things besides allocating
memory:
It sets the object’s retain count to one (as described in “How Memory
Management Works”).
Therefore, you don't need to retain something that you've just alloc'ed.
Adding to Felz answer above. Use self.realArray when allocating array
self.realArray = [[NSMutableArray alloc] init];
Because you have created a property for the array so it is better to use "self"
You could also take advantage of the properties in objective C to make
more clear and efficient your code:
NSStringArray.h
#interface NSStringArray : NSObject {
}
#property (nonatomic, retain) NSMutableArray *realArray;
#end
NSStringArray.m
#import "NSStringArray.h"
#implementation NSStringArray
#synthesize realArray = _realArray;
-(id)init {
self = [super init];
if (self) {
self.realArray = [NSMutableArray array];
}
return self;
}
-(void)dealloc {
[_realArray release];
[super dealloc];
}
Now, with the modifier retain of the property realArray you can use
[NSMutableArray array] that return an autorelease mutable array.
The retain properties manage the retain/release stuff by themselves.
You don't need to use the realArray = nil; line. You've already deallocated
the property.
Hope this can help.
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.
Assume I have a interface like:
#interface it:NSObject
{
NSString* string;
}
#end
#implement it
-(id)init
{
if(self = [super init]){
self.string = [[NSString alloc]init];
}
}
-(void)dealloc
{
[self.string release];
[super release];
}
#end
If I use this class in another file and I call this:
it ait = [[it allow] init];
NSString* anotherString = [[NSString alloc] init];
ait.string = anotherString;
[anotherString release];
Will this cause the string alloced in init method of it a memory leak?
Since the string is not referenced and not autoreleased.
If I don't alloc a string in the init method, what will happen when I call ait.string right before assign anotherString to it?
I think you need
#property (nonatomic, retain) NSString *string;
in your interface and
#synthesize string;
in your implementation for self.string to work.
Then, when you do
self.string = [[NSString alloc] init];
in your init method the string will actually have a retain count of 2 because [[NSString alloc] init]will return a string with a retain count of 1, and using self.string = will retain the object again because the property is declared as 'retain'. This will result in a memory leak, but you can fix by having:
-(id)init
{
if(self = [super init]){
self.string = #"initString";
}
}
or similar.
Then, onto the actual question, if you do as above the string allocated in init wont leak when you reassign with self.string = because properties marked as 'retain' release their current object before retaining the new one.
If you don't assign a string to self.string in the init method it doesn't matter because self.string will just return nil, which you can then deal with in your program.
Will this cause the string alloced in
init method of it a memory leak? Since
the string is not referenced and not
autoreleased.
Yes, exactly. You seem to have got it.
If I don't alloc a string in the init
method, what will happen when I call
ait.string right before assign
anotherString to it?
Do you mean in the following case you are unsure what unknownObject would refer to?:-
It *ait = [[It allow] init];
NSString *unknownObject = ait.string;
This is nonsense. Did you forget to add the accessor methods?
Objective-c doesn't use 'dot syntax' to access instance variables, like, say, Java. If you have an instance of your class 'it' you can only access the 'string' variable from outside that instance by calling the accessor 'getter' method. This is not optional self.string is just a shortcut for the method call [self string] and this method doesn't exist in the code you showed.
Assuming the accessor method are defined elsewhere, the instance variable you called string (this is the worlds worst variable name) is equal to nil. In Objective-c you have to treat nil objects very carefully as the behaviour is different from how many other languages treat the similar null.
In Objective-c this is fine:
NSString *nilString = nil;
[nilString writeToFile:#"/this_file_cannot_exist.data"];
Many other languages would crash here or throw an exception; This can be dangerous, as an operation may fail but your app will continue running. It can also be great tho, because in other languages you will see lots of this..
someObject = controller.currentValue()
if( someObject!=null )
someObject.writeToFile("myFile.data")
In Objective-c the 'if(..)' line isn't needed at all.
You must be careful not to call the accessor method inside your init and dealloc methods, as this can break subclasses. Instead of
- (void)dealloc {
[self.string release]; // This is [[self string] release]
...
you should just use
- (void)dealloc {
[string release];
...
As well as being dangerous the call to [self string] is unnecessary. The same is true in your init methods
if(self=[super init]){
self.string = [[NSString alloc]init]; // shortcut for [self setString:[[NSString alloc] init]]
...
just use
if(self=[super init]){
string = [[NSString alloc] init];
...
You're missing the #property in order to use self.string.
Add this to your .h file
#property (readwrite, copy) NSString *string;
Using Copy instead of Retain will prevent the string from memory-leak even if you release anotherString.
#interface it:NSObject
{
NSString* string;
}
//you should declare a property in order to call it with 'self' prefix
#property (nonatomic, retain) NSString* string;
#end
#implementation it
//you should synthesize your property
#synthesize string;
-(id)init
{
if(self = [super init]){
//you don't to initialize NSString right here (memory leak will have place)
//self.string = [[NSString alloc]init];
//you can store a default value like this (can be omitted):
self.string = #"Default value";
}
return self;
}
-(void)dealloc
{
[self.string release];
[super release];
}
#end
And everything regarding memory management in this class will be fine.
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.
I'm new to Objective-c. For learning purposes I'm trying to build something like a phonebook. So I'll have a class called Person that will have some properties (name, phone, etc).
Right now I'm not preoccupied about persistence. But, I need something to "hold" Person objects. So I thought about create a class called People, but I don't know how to design it, specially the NSMutableArray that will hold the objects.
What I did was:
PERSON.H
#interface Person : NSObject {
NSString *name;
}
#property(readwrite, copy) NSString *name;
#end
PERSON.M
#implementation Person
#synthesize name;
#end
PEOPLE.H
#interface People : NSObject {
NSMutableArray *peopleArray;
}
#property(readwrite, retain) NSMutableArray *peopleArray;
- (void)addPerson:(Person *)objPerson;
#end
PEOPLE.M
#implementation People
#synthesize peopleArray;
- (id)init {
if (![super init]) {
return nil;
}
peopleArray = [[NSMutableArray alloc] retain];
return self;
}
- (void)addPerson:(Person *)objPerson {
[peopleArray addObject:objPerson];
}
PHONEBOOK.M
...
Person *pOne = [[Person alloc] init];
pOne.name =#"JaneDoe";
People *people = [[People alloc] init];
[people addPerson:pOne];
When I try to use this code, I receive an error:_method sent to an uninitialized mutable array object.
So, since I'm a newbie, probably the way that I did isn't the best/correct one. So, how do I do this?
Two things wrong with your initialiser for people. It should look more like this:
- (id)init {
self = [super init]; // always assign the result of [super init] to self.
if (!self) {
return nil;
}
peopleArray = [[NSMutableArray alloc] init]; // use init not retain.
return self;
}
Because you're not calling init on the NSMutableArray when you create your peopleArray. Try calling:
peopleArray = [[NSMutableArray alloc] init];
instead. You need not retain it unless you say for instance, did this:
peopleArray = [[NSMutableArray array] retain];
For reasons why, see the rules for memory management. These rules are very important for any iPhone or Mac developer to understand, and quite frankly, are simple enough that there's no excuse. :)
In People.m you probably meant to write
peopleArray = [[NSMutableArray alloc] init];
instead of
peopleArray = [[NSMutableArray alloc] retain];