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.
Related
I have declared a NSMutableArray as a singleton; when I try to check for the array count, the app crashes! Here is the code:
// clear array that holds selected servcies
SingletonArrayOfSelectedRows *arrayOfSelectedRows = [SingletonArrayOfSelectedRows sharedArrayOfSelectedRows];
if([arrayOfSelectedRows count] > 0)
[arrayOfSelectedRows removeAllObjects];
This code is the same code I have found all over SO and Google. Using XCode5, I have checked to make sure the singleton is allocated (and it is), and there is a valid count (0) for the singleton.
UPDATE
Here is the code for the singleton.h file:
#interface SingletonArrayOfSelectedRows : NSMutableArray {
}
#property (nonatomic, retain) NSMutableArray *arrayOfSelectedRows;
+ (id)sharedArrayOfSelectedRows;
#end
Here is the code for the singleton.m file:
#implementation SingletonArrayOfSelectedRows {
}
#synthesize arrayOfSelectedRows; // rename
// sharedSelectedCellIndexes
+ (id)sharedArrayOfSelectedRows {
static dispatch_once_t dispatchOncePredicate = 0;
__strong static id _sharedObject = nil;
dispatch_once(&dispatchOncePredicate, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
-(id) init {
self = [super init];
if (self) {
arrayOfSelectedRows = [[NSMutableArray alloc] init];
}
return self;
}
#end
Don't subclass NSMutableArray to do this. NSMutableArray is a class cluster. All of the actual array implementation is inside subclasses of NSMutableArray. If you subclass NSMutableArray then your subclass won't actually implement any array behavior unless you write it yourself.
According to the documentation :
Any subclass of NSArray must override the primitive instance methods count and objectAtIndex:.
Since you are subclassing NSMutableArray you will need to override the following NSMutableArray primitive methods as well:
insertObject:atIndex:
removeObjectAtIndex:
addObject:
removeLastObject
replaceObjectAtIndex:withObject:
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];
I have the following properties defined:
#property (nonatomic, retain) NSString *name_;
#property (nonatomic, retain) NSString *profilePicture_;
#property (nonatomic, retain) NSString *username_;
#property (nonatomic, retain) NSString *id_;
and I set them up in my init... like this:
-(id)initWithData:(NSDictionary *)data
{
self = [super init];
if (!self) {
return nil;
}
name_ = [data valueForKey:#"full_name"];
profilePicture_ = [data valueForKey:#"profile_picture"];
username_ = [data valueForKey:#"username"];
id_ = [data valueForKey:#"id"];
return self;
}
with the following dealloc:
-(void)dealloc
{
[name_ release];
[username_ release];
[profilePicture_ release];
[id_ release];
[super dealloc];
}
However the dealloc gives me an error:
pointer being freed was not allocated
Why is this? Do I have to do [[NSString alloc] init...] or [NSString stringWithString:]?
valueForKey: will return an autoreleased object, therefore you have no ownership. As they are all strings you can just call copy like this
name_ = [[data valueForKey:#"full_name"] copy];
profilePicture_ = [[data valueForKey:#"profile_picture"] copy];
username_ = [[data valueForKey:#"username"] copy];
id_ = [[data valueForKey:#"id"] copy];
you should also change your #property declarations to use copy as this is generally recommended for strings.
The other alternative is to go through the synthesised accessors but I generally avoid doing this in either init or dealloc
This is because you are assigning to backing variables in your initWithData. You should use rewrite your code as follows:
self.name_ = [data valueForKey:#"full_name"];
self.profilePicture_ = [data valueForKey:#"profile_picture"];
self.username_ = [data valueForKey:#"username"];
self.id_ = [data valueForKey:#"id"];
This would assign values through properties, which calls [retain] for you. The way your code is written now, the pointer is simply copied into ivars without calling [retain], which ultimately causes the issue that you describe.
Since you are using data from dictionary, you should set values through properties using self keyword.
If it doesn't solve, then problem is probably not inside your class but perhaps where you create the instance of it. Try examining the Code where you allocate and release the instance of this Class.
You should also profile your app using Simulator & NSZombies n Determine where you over-release the object.
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.