Instruments not detecting obvious memory leak - objective-c

I have created a very obvious memory leak in application didFinishLaunchingWithOptions.
NSArray *temp = [[NSArray alloc] initWithArray:[[NSArray alloc] initWithArray:#[]]];
temp = [[NSArray alloc] initWithArray:#[]];
temp = nil;
However the tools show no memory leaks at any point, which leads me to believe there not working properly. Anybody else experienced this?

From my observation: the empty NSArray is basically a singleton instance. You can't create memory leaks in this way, because your app will always have a reference to the empty array "singleton".
This will show you that all empty arrays point to the same memory address:
NSArray *array1 = [NSArray array];
NSArray *array2 = [NSArray arrayWithArray:array1];
NSArray *array3 = [NSArray arrayWithArray:#[]];
NSArray *array4 = #[];
NSArray *array5 = [#[] copy];
NSArray *array6 = [[NSArray alloc] initWithArray:[[NSArray alloc] initWithArray:#[]]];
NSLog(#"%p", array1);
NSLog(#"%p", array2);
NSLog(#"%p", array3);
NSLog(#"%p", array4);
NSLog(#"%p", array5);
NSLog(#"%p", array6);
No matter where in your application lifecycle you log the address of the empty array, it will always be the same.
You should try your test with NSMutableArray. Or, even better, use an class you created yourself.
Sometimes there is heavy optimization going on in the background if you use built in classes. For example class clusters, where you can't be sure what exact class an initializer returns, or singleton instances like in this case.

Related

NSArray copy for undo /redo purposes

If I have an nsarray full of custom objects and I make a second array using:
NSArray *temp = [NSArray arrayWithArray:original];
then work with some properties of the objects inside the original array, then decide to roll back, I am then using the reverse:
original = [NSArray arrayWithArray:temp];
I am finding the objects I changed in the array also effected my temp array. I also tried implementing copyWithZone on my custom class, and using copyItems and it did not help. What else should I try?
To be clear, in order to use copyWithZone, I changed my array creation command to:
NSArray *temp = [[NSArray alloc] initWithArray:original copyItems:YES];
My copyWithZone:
-(id)copyWithZone:(NSZone *)zone{
CustomObject *ret = [[CustomObject allocWithZone: zone] init];
//copy properties
return ret;
}

Objective C: Releasing a list, will it cause potential leaks?

Tried finding the answer online, but couldn't. So i'm wondering if anyone else knows and why?
Say I have an NSDictionary, or NSArray, that stores objects inside of them. If I release the NSDictionary, is there a potential leak because I didn't release the objects inside of the NSDictionary list?
For example:
NSDictionary *dict = [NSDictionary alloc] init];
// Create a bunch of objects, NSStrings, etc.
// Store it into dict.
[dict release];
Will that also release everything inside of the dict? (objects, nsstrings, etc).
Thanks in advance people!
All items in an NSDictionary or NSArray are automatically retained when they're added and released when removed, or when the list is destroyed.
For example:
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
MyObject *obj = [[MyObject alloc] init];
[dict setObject:obj forKey:#"foo"]; // the dictionary retains "obj"
[obj release]; // this matches the "alloc/init"
// but "obj" still is retained by the dictionary
[dict release]; // now "obj" gets released
When you do a release on a NSDictionary or NSArray, as long as the retain count of the objects inside the array is 1 (meaning as long as you released the objects after you inserted them inside the data structure), then once you release the dictionary or array, those objects will be released as well.

Does NSString componentsSeparatedByString: return autoreleased array?

In the following method, I'm unsure of why releasing one of the arrays leads to an exception. The only reason that I could see, would be if componentsSeparatedByString returns an autoreleased array, but I can't see that the documentation mentions that it do.
-(void)addRow:(NSString *)stringWithNumbers;
{
NSArray *numbers = [stringWithNumbers componentsSeparatedByString:#" "];
NSMutableArray *row = [[NSMutableArray alloc] initWithCapacity:[numbers count]];
for (NSString *number in numbers) {
Number *n = [[Number alloc] initWithNumber:number];
[row addObject:n];
[n release];
}
[rows addObject:row];
[row release];
// [numbers release]; <-- leads to exception
}
Can anyone confirm if the array is autoreleased? If so, how can I know/why should I have known?
Is it possible to check if any one instance of an object is autoreleased or not by code?
Yes, because the name of the method:
does not start with new
does not start with alloc
is not retain
does not contain copy
This is commonly known as the "NARC" rule, and is fully explained here: http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmObjectOwnership.html#//apple_ref/doc/uid/20000043-SW1
unless you specifically allocate memory, a system method will give you back an autoreleased method.
By convention all methods with init or copy in their names return non-autoreleased objects.

NSString EXC_BAD_ACCESS

I'm stuck with the following bit of code.
NSString *gridRef = [[NSString alloc] initWithFormat: #"%#", [converter LatLongToOSGrid: latLong]];
NSLog(#"Grid Ref: %#", gridRef);
self.answerLabel.text = [[NSString alloc] initWithFormat: #"%#", gridRef];
When I log gridRef, it displays the correct result. However, the line setting answerLabel.text causes an EXC_BAD_ACCESS error and the program crashes. IB is connected to the correct label, what is the problem?
Thanks
I've updated the code as follows:
- (IBAction)convertLatLong {
NSArray *latLong = [[NSArray alloc] initWithObjects: latTextField.text, longTextField.text, nil];
GridRefsConverter *converter = [[GridRefsConverter alloc] init];
NSString *gridRef = [[NSString alloc] initWithFormat: #"%#", [converter LatLongToOSGrid: latLong]];
NSLog(#"Grid Ref: %#", gridRef);
NSLog(#"Label: %#", self.answerLabel.text);
answerLabel.text = #"Yippy";
self.answerLabel.text = gridRef;
[gridRef release];
[converter release];
[latLong release];
}
answerLabel is initialised through #property #synthesize when the view controller is pushed onto the stack. (I don't know how it gets init'd apart from it's one of the magical things IB does for you. Or so I assume. I've used exactly the same method in other view controllers and have not had this issue.
I've found the culprits - the question is, how do I go about releasing them?
NSString *eString = [[NSString alloc] initWithFormat: #"%f", e];
NSString *nString = [[NSString alloc] initWithFormat: #"%f", n];
eString = [eString stringByPaddingToLength: (digits/2) withString: #"0" startingAtIndex: 0];
nString = [nString stringByPaddingToLength: (digits/2) withString: #"0" startingAtIndex: 0];
NSString *theGridRef = [letterPair stringByAppendingString: eString];
theGridRef = [theGridRef stringByAppendingString: nString];
[eString release];
[nString release];
return theGridRef;
and:
NSArray *gridRef = [[NSArray alloc] init];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithDouble: E]];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithDouble: N]];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithInteger: 8]];
NSString *theGridRef = [[NSString alloc] initWithFormat: #"%#", [self gridRefNumberToLetter: gridRef]];
[gridRef release];
[theGridRef autorelease];
return theGridRef;
}
You should enable zombie detection by setting the environment variable NSZombieEnabled to YES, so you can see which object causes the bad access (don't forget to remove this again when you found the bug).
Also you can use Instruments to find the location where the object actually gets released. For this start a new Instruments session and use the "Allocations" instrument. In the instrument settings check "Enable NSZombie detection" and "Record reference counts". When running the session you will break where the error occurs and you see a record of all retains/releases.
One place where you can have a quick look if your object is incorrectly freed is in the -viewDidUnload method, where you should release the outlet and set it to nil. If you forget the latter and you access the outlet somehow, it will result in a EXC_BAD_ACCESS.
Edited to match your update:
The problem is that you are assigning eString (and nString) a new string which was alloc/init-ed. Then you override those in the next statements, because -stringByPaddingToLength: (as well as all the other -stringBy... methods) return a new and autoreleased string object. So you lost the reference to the old string which means that there is a memory leak. Additionally at the end you release the already autoreleased objects explicitly which causes your bad access.
Instead you should create autoreleased strings from the beginning ([NSString stringWithFormat:...]) and don't release them at the end.
Check if asnwerLabel is actually non-null. You should also change this line:
self.answerLabel.text = [[NSString alloc] initWithFormat: #"%#", gridRef];
To:
self.answerLabel.text = [NSString stringWithFormat: #"%#", gridRef];
Otherwise, you will end up with a memory leak in that line.
Maybe the label is not inited at that point in your code, try to check it. Why are you allocating a new NSString?
Just do:
self.label.text = gridRef;
[gridRef release];
how is answerLabel created? You might need to retain that. Or you possibly need to release some things (gridRef)?
I can't see any other issues with your code.
You can (and probably should) set your
answerLabel.text = gridRef;
gridRef is already an NSString, so you don't need to alloc it again.
EXC_BAD_ACCESS is usually a memory thing related to your retain/release count not balancing (or in my extensive experience of it :p).
Okay, the problem was trying to release NSStrings, so I've stopped doing that and the problem has been solved.
Can someone clarify how strings are retained and released. I was of the impression that:
string = #"My String"; is autoreleased.
NSString *string = [[NSString alloc] init...]; is not autoreleased and needs to be done manually.

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.