I wrote a test code to check how to use Instrument (Leaks). I have created a single view application and on a button click I have loaded a new view like this...
- (IBAction)btn_clkd:(id)sender {
new_file *new = [[new_file alloc] init];
if (new) {
[self.navigationController pushViewController:new animated:YES];
new = nil;
}
}
In new_file ViewDidLoad method, I have create a leak like below...
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
char *c_mem = (char*) malloc(10000000);
strcpy(c_mem, "TESTING");
// free(c_mem);
}
Even I'm using ARC, memory allocated is a plain C malloc, and I have not freed memory after used, even though I have popped and loaded again and again the new view, instrument(Leaks) is not detecting any leak in this code... What is the reason, Am I checking correctly?
Thanx
Given your view controller is deallocated (please verify), eventually Leaks will detect that c_mem pointer leaks.
Instruments may not find this immediately - due to the algorithm it uses to detect leaks.
The algorithm is basically searching for unreferenced variables looking like pointers which point into the heap. If it happens that there is an arbitrary variable, say p, whose value happens to be the value of c_mem - then Instruments thinks that pointer c_mem is still referenced from p -- even though p may contain only arbitrary data and not referencing c_mem.
Note that Instruments leak detecting algorithm is actually more sophisticated, and may change and get improved.
In order to find leaks, I always run special code - like unit tests - in a loop. If the used memory does not grow in time, you are usually good. You can also use the "Snapshot" feature of Leaks, and the "Mark Generation" feature of Allocations to check for the current state of the heap.
When you pop your ViewController, if nothing has references to it, it is deallocated automatically by ARC with all it's variables. That is why you do not get leaks.
Look at what happens to your controller, when you pop it, using Leak Instrument. Also you can see there a number of references on it. It is very convenient to detect retain cycles also.
Here is a full reference on how to use Instruments.
Related
When profiling the SimpleEKDemo application from Apple I note there are some memory leaks.
One of the leaks is __NSArrayM which has 3 lines in the Leaked Blocks history, a Malloc/Assign/Release.
Question - can someone point out the root cause issue here? (I'm trying learn how to take Instruments output of where a leaky object was created, and then from there work out root cause, so this would be really useful)
You will notice that when you run the demo with leaks that there is a leak in viewDidLoad (responsible frame). If you switch to Call Tree detail and if you have enabled Invert Call Tree, you will see a leak associated with the call +[NSArray new]. If you open that a bit, you will see initWithArray which is called in the RootViewController's viewDidLoad. The problem bit is,
self.eventsList = [[NSMutableArray alloc] initWithArray:0];
eventsList is a retained property so the object created has a retain count of 2. However it is only released once either through the release in dealloc or through reassignment of eventsList. YOu will have to autorelease this object.
self.eventsList = [[[NSMutableArray alloc] initWithArray:0] autorelease];
Once fixed, you shouldn't get any errors.
- (void)btnInboxPressed
{
for (int j = 0; j < 100000; j++) {
[[UIButton alloc] init];
}
}
Why would this not show up as a memory leak in Instruments? If I alloc NSMutableString in the same fashion, Instruments flags a memory leak.
It may be because there is something somewhere that is still pointing to each button; still has a reachable reference to each button instance.
A "leak" is an object (or allocation) for which there is no way the program can every refer to it again. The address of said object no longer appears anywhere in the program.
There are, however, a multitude of ways you can accrete memory without it being counted as a leak.
Write-only caches are a classic failure pattern. So are registries where every instance is registered with some central scrutinizer. The central scrutinizer still has a valid reference -- thus not a leak -- even though you don't need the object again.
You are doing exactly what you should; "Holy cow! Why does instruments show that I have 10,000 UIButtons!?!?!? If I figure that out and make 'em go away, my memory use will drop significantly!!"
In this specific case, it might be that the autorelease pool still has a reference to the object. It might be that allocation and initialization of a UIButton causes it to be retain/autoreleased somewhere along the way.
No way to know without knowing where in the event loop that screenshot was grabbed.
This is a very very... very odd bug. It's hard to describe the exact project I have, but I will try and create a simpler representation of the classes I have. It goes like this:
Assume I have a navigation controller as my top view controller. Inside it, at one moment in time I have a UIViewController, let's say a ContactsScreenController. The view for this contains multiple UITableView that each is controlled by a separate object of type MyTableController (delegate&datasource). I do this by keeping an array of controllers
// This is the interface for my screen controller. An object of this type goes in a top-
// level navigation controller
// MainScreenController.h
#interface ContactsScreenController : UIViewController
NSMutableArray* tableControllers;
#end
// MainScreenController.m
- (UITableViewCell*)cellForRowAtIndexPath..something..
{
// Here what I do is create a new controller if needed, and add it to tableControllers
// Memory allocations & releases are good because I checked with instruments
}
#define SAFE_DEL(x) { if (x != nil) { [x release]; x = nil; } }
- (void)dealloc
{
SAFE_DEL(tableControllers);
[super dealloc];
}
Now, MyTableController is a more complicated object as it handles fetching data from a web service, but basically what I do is I want to make sure that when the object is deleted, I cancel any pending data requests, like this:
// MyTableController.m
- (void)dealloc
{
[globalDataProvider cancelRequestsForController:self];
// release other objects i might have
[super dealloc];
}
OK, so this is my objects setup. The crash occurs when I am deleting the object tableControllers. It decrements the retainCount for my MyTableController objects and it reaches 0 (checked using Instruments). But for some UNKNOWN reason, I get calls for cancelRequestsForController, AFTER the retain count has been zero.
Obviously, I get a EXC_BAD_ACCESS.
Before you start thinking it's a problem with my retain/release pairs, the application runs perfectly if I am releasing the main screen controller while the inner tables are static. As soon as the are scrolling and I hit the Back button in the navigation controller I experience the bug.
I've checked using instruments the entire history of retain count changes for my inner controllers and it is good (no unusual stuff). When the bug occurs, my last entry in the history is from QuartzCore run_animation_callbacks with a retain count of -1.
Any ideas? :)
PS: As a quick solution to get the project going, I've moved the cancelRequestsForController in a separate method and I'm manually calling it for each object in tableControllers before the release. This way I am sure that there will be no calls after the release, no matter the state of the tableview.
- (void)dealloc
{
for (TableController* c in tableControllers)
[c cancelRequests];
SAFE_DEL(tableControllers);
[super dealloc];
}
But I don't like this solution for several reasons.
Thanks to everybody for the answers/comments.
The problem was generated by a call to [super dealloc] in an object, before I got the chance to release my objects. This caused a lot of crazy stuff to happen. I moved the [super dealloc] at the end of my dealloc method (after my releases) and it works fine now.
The SAFE_DEL macro is unnecessary and makes the code less readable. Simply do:
[someObject release], someObject = nil;
It won't matter if someObject is already nil and it makes the code more directly readable.
As soon as the are scrolling and I hit
the Back button in the navigation
controller I experience the bug.
Any time you have non-memory management logic, you have fragility. Namely, when dealloc is being executed, it is quite likely because there is an entire sub-graph of objects in your application that are being deallocated. Since deallocation order is largely non-deterministic, you can't safely trigger any complex behavior in dealloc without risk that you are going to message an already deallocated object.
The best solution would be to get that cancellation mechanism out of dealloc!
Two things to check:
1. does your globalDataProvider (or any other class for that matter) have a reference to your controller (i.e. to call it back with data?) Your retain counts could be zero but for the wrong reason. How are you allocating the array, as well as each controller in the array? Do you have #properties?
2. In your executables properties Arguements screen, set NSZombieEnabled=YES That will tell you exactly which object is being called when it has a zero retain count.
HTH,
-Mike
In the code below, PersonListArray is an NSMutableArray and I'm getting the list of persons from the sqlite DB and adding it to my array.
Person* tmpPerson = [[Person alloc] init];
tmpPerson.personName = #"Mike";
tmpPerson.personEmail = #"mike#mike.com";
[PersonListArray addObject:tmpPerson];
[tmpPerson release];
Even though I'm releasing the Person object here, its giving a memory leak which I'm guessing is due to the array holding a reference count to it. I'm using the array elsewhere in the program and then releasing it for sure.
What's the best practice to create new objects for an array and not run into this issue?
In the dealloc method where i release the array
-(void) dealloc{
[PersonListArray release]; // this contains the numerous Person objects
[super dealloc];
}
should i manually release them like this instead ?
-(void) dealloc{
for (int i = 0; i<PersonListArray.count;i++)
{
Person * tmpPerson = [PersonListArray objectAtIndex:i];
[tmpPerson release];
}
[PersonListArray release];
[super dealloc];
}
The code you are showing us is correct and contains no leaks. The last section is wrong, though, and would case your program to crash because you are releasing Person objects you no longer own.
Your code, as initially implemented, is correct. An array retains onjects added to it and releases them either when they're removed from the array or when the array is dealloced. No need to go through the array yourself.
What means are you using to detect the leak? If it's Instruments then you may be misunderstanding what it is telling you. When it detects a leak, it can show you where the memory was first allocated. It can't show you which object is responsible for the leak. I would therefore guess the given dealloc method is never called (because that object is leaked) or that someone else retains the array and doesn't release it. Try putting an NSLog in dealloc to ensure that it is occurring; as a run once test you could try logging PersonListArray after releasing it — if that doesn't cause a memory exception then almost certainly someone else has retained it.
[REMOVED: my original text "Try adding an NSLog of [PersonListArray retainCount] to your dealloc to figure out which is the case."; see comment from bbum below]
The most common cause of accidental additional retains is #property/#sythesize properties that are set to retain but for which a matching release is not added to dealloc.
Somewhere else in your app, you probably call [PersonListArray objectAtIndex:n] and pass it around to various other parts of your app. One of the other parts of your app is probably leaking it.
If you're using leaks, click on the particular "type of leak", then click on the memory address, and it'll show you the alloc/free/retain/release/autorelease history of that memory address. If you enable the detail view (Cmd-E I think), you'll see stack traces for all of those as well. Look for something that's doing a retain but not a corresponding release. (It's a bit difficult when things are retained by multiple autoreleased arrays...)
I'm diving into iOS development and am getting familiar with the tools. At the end of every day, I perform a "Run with instruments tool -> Leaks" on my app to check for any memory leaks I may have implemented that day. It rarely seems to detect any leaks and, while I'd like to think I'm just a natural iOS programmer, I refuse to believe that ;)
Anyhow, I just found what I think is a memory leak in my code and it doesn't get caught by Instruments. I have this line of code...
gkSession = [[GKSession alloc] initWithSessionID:#"testID" displayName:#"Temp Display Name" sessionMode:GKSessionModeClient];
and I found that I wasn't calling release anywhere in my code. My questions are...
Is this a memory leak?
If so, what are some reasons that Instruments might not catch it?
My obvious concern is that I have memory leaks in my code and Instruments isn't catching them.
Thanks so much in advance for your help!
There are multiple types of dynamically allocated memory.
Memory with a reference count
greater than zero which is still referenced and is in use.
Memory with a reference count
of zero which is still referenced and is still in use.
Memory with a reference count greater than zero which is not referenced.
Memory with a reference count of zero which is not referenced.
Memory with a reference count
greater than zero which is still referenced and is NOT in use.
Type one is normal in use memory. Type two is a bug that will be reported as an illegal access when you try to follow the reference. Type 3 is the type of leak that instruments detects. Type 4 should be freed by the memory system.
Type 5 is a leak which cannot be detected by instruments, and will also not be handled by a full garbage collector. This is what you seem to have.
EDIT:
I forgot type 6 -- Memory with a reference count that doesn't match the number of actual references. This will probably eventually turn into type 2 or 4.
The answer to #1 is "maybe"... if your view controller gets popped off the stack, and no one else has retained it, then your view controller should get deallocated. But, (and this may be the answer to your question #2) one of the mistakes I made originally was not understanding that when you call pushViewController: the navigation controller will retain your view controller, so you don't have to. Make sure you are releasing your view controller after you create it and push it on the stack.
MyViewController * viewController = [[MyViewController alloc] init...];
[self.navigationController pushViewController:viewController];
[viewController release];
I believe the static analyzer will warn you if you forget the release call in this situation... which is one reason the static analyzer is so great.
If you don't release the view controller at this point, your retain count will always be +1 higher than it should.
I've noticed the static analyzer is MUCH more useful for these things than the instruments' leak tool. (Which, for example, won't find a "leak" that includes circular references... two leaked objects that reference themselves don't show up because "a reference" is still around for your object).
So currently you have something along the lines of...
#interface MyClass : UIViewController
{
GKSession * gkSession;
}
...
#end
in your implementation you should make sure you release your iVar in your dealloc method:
#implementation MyClass
...
- (void)dealloc
{
[super dealloc];
if (gkSession) [gkSession release];
}
#end