How do I debug a iPhone SDK memory reallocation issue? - iphone

I have just been through a client project initially coded by us then abused by them! And we are getting a strange crash, which is consisten on each mac individually but inconsistent between the team or after a reload/restart of Xcode/OSX.
The problem is one I have come across before when I havent been cleaning up objects properly, but I have been through static analyser and am still struggling.
The crashes always seem to occur because objects we have created in the VC are getting overwritten by others therefore changing their type so valid method calls are being thrown as exceptions.
For example:
NSNumber *test1;
-(void)viewDidLoad {
test1 = [NSNumber numberWithInteger:7];
}
-(void)someOtherMethod {
NSLog(#"what was test? %#", [test stringValue]);
}
This is currently throwing an unrecognized selector exception during the NSLog as hovering over test1 tells me is is of type NSURLRequestInternal, if I restart Xcode it will probably be somewhere else!!
How can I debug/solve this!?!?

That should crash every time.
Let's look at the code.
test1 = [NSNumber numberWithInteger:7];
This creates a NSNumber and assigns it to the instance. However, the NSNumber is scheduled for deallocation as soon as the main event loop is run.
NSLog(#"what was test? %#", [test stringValue]);
This, presumably, is being run after the main event loop has had a swing. At this point, test (and I assume this should be test1) is pointing to a dangling pointer. Accessing it will crash.
There is no substitute to reading Apple's documentation on memory management: http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
This will explain what you need to know. But basically, you should be using [[NSNumber alloc] initWithInteger:7] or retain it (possibly using property syntax) instead.
But unless you read Apple's documentation on the subject, you're going to be chasing memory bugs forever.

you forgot to retain test1 so it gets autoreleased, hence the crash
try using NSZombieEnabled for debugging this kind of stuff.

test1 = [NSNumber numberWithInteger:7];
should either be
test1 = [[NSNumber numberWithInteger:7] retain];
or (better)
test1 = [[NSNumber alloc] initWithInteger:7];
numberWithInteger: returns an autoreleased object, so by the time someOtherMethod is called, the object that test1 points to has been deallocated.

Related

Objective-C release is not called explicitly

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * str = [[NSString alloc] initWithString:#"test"];
[str release];
int i = 999999999;
while(i-- > 0) {}
NSLog(#"%#", str);
[pool drain];
Output: test
Why didn't release work?
How can I immediately delete the object from memory?
Xcode Version 4.0 iPhone Application
~SOLVED~
Thank's to all for answers.
I've got a lot of useful information about this question. I'm going to use NSString *str = #"text" instead of NSString *str = [[NSString alloc] initWithString:#"text"];
i've understood that release just "marks" memory as "willing to be freed", but not freeing it immediatly
It did work. You have relinquished ownership of that object, and when the system determines that it is no longer owned, it will be marked available for reuse by the system. That may happen immediately, if you were the only owner of the string. It may happen at some later point, if creation of the string caused it to be autoreleased internally. Or, as Dave DeLong points out, the system may optimize it into an object that is never released.
In your case, it's being optimized into a constant string, which will exist for the life of the program. If you were to use an NSMutableString instead of an NSString, you'd see funky behavior that would probably not crash, but wouldn't print what you expected. (See this question for an example.)
If you used an NSArray instead, it would be deallocated when you called release, but you'd still see your NSLog example work correctly until you allocated some other object. Deallocation just marks the memory as available for reuse; it doesn't actually clear it out. So if you passed the array to NSLog, that memory hasn't been changed and thus it still prints correctly.
The key point in all of this, though, is to recognize that calling release will not necessarily cause the object to be deallocated. It may continue to exist for any number of reasons. But once you call release, you have relinquished ownership of the object. If you continue using it after that point, the system is free to do all sorts of weird things at its own will, as demonstrated.
Release does work but what you are attempting to do has undefined behavior, and when using a NSString and a literal you may also get different behavior. What is happening is although your object is released the memory at that location is reclaimable and has not changed and when it goes to print it it is still valid. Since it is a NSString a message to description is not necessarily sent and that is why you are not getting an exception for attempting to message a deallocated object.
This question has some good information about NSString and NSLog.
When you do:
NSString * str = [[NSString alloc] initWithString:#"test"];
This gets optimized into:
NSString * str = #"test";
You can't release a constant string, because it's hardcoded into the application binary.
Proof:
NSString *s = [NSString alloc];
NSLog(#"%p", s);
s = [s initWithString:#"foo"];
NSLog(#"%p", s);
s = #"foo";
NSLog(#"%p", s);
Logs:
2011-04-12 10:17:45.591 EmptyFoundation[6679:a0f] 0x100116370
2011-04-12 10:17:45.599 EmptyFoundation[6679:a0f] 0x100009270
2011-04-12 10:17:45.604 EmptyFoundation[6679:a0f] 0x100009270
You can see that the result of +alloc is different from the result of -initWithString:, and the result of -initWithString: is equivalent to the constant string. Basically, -initWithString: says "aha, i'm going to be an immutable string, and I'm being given an immutable string! I can just take a shortcut, destroy myself, and return the parameter, and everything will still work the same"
You're using a bad pointer in you NSLog(). You happen to be getting lucky in this case, but you should expect code like this to crash or fail in other ways.
There is no need to delete the memory block, this will use up an unneeded cycle.
The memory will be overridden when an new object is allocated an occupy that memory block.

Memory leaks caused due to CoreFoundation Framework

I am developing an iPhone application which mainly makes use of the Address Book and database. After fetching about 3000 contacts from the address book, I am attaching string tags to the contacts (5 for each). I am saving my tags in the database.
For Load testing purpose i have added 10,000 tags to the App. But during the load testing of my application, I observed some memory leaks which were not related to the Application code but represents a set of Instruction sets. Also Instruments showed Foundation as the responsible library for the Leak (Extensive use of NSString,NSDictionary,NSArray which belongs to the Foundation framework). My application crashes after 10 - 15 mins of usage.The Crash report mentions, application crashed due to low memory.
Memory profiling using CLANG shows zero leaks. How do i solve these memory leaks?
Are these leaks the real culprit behind the crash? Are there any other tools available to check memory leaks?
I often find my leaks say they're caused by Core Foundation (or any other framework for that matter) but are really my own. With the exception of the Simulator, rarely will you find excessive leaking in the frameworks.
If you open up the detail panel to the right in Instruments you may find listed your App's methods in there. That will give you indication of where it could be coming from in your code. One leak can spring many other leaks, and you may have to find the top level culprit to get rid of the lower level ones.
You should not expect Clang to do anything but find the most obvious leaks. It's very handy, but that's it, just a helpful addition to compiling.
clang is not a leak checker. It only detects a small subset of issues.
For memory leak debugging you should focus on Instruments, specifically the Object Allocation and Leaks instruments. Be sure to understand the difference between leaks and other source of high memory usage though.
Once you've determined that objects are leaking, use Instruments to examine their allocation stack trace (so you can tell what object it is), and their retain/release history.
If it's not a leak, then I suggest investigating the instructions here:http://www.friday.com/bbum/2010/10/17/when-is-a-leak-not-a-leak-using-heapshot-analysis-to-find-undesirable-memory-growth/
Most likely you have code that is creating the foundation objects. Leaks shows you the place of allocation but that is generally due to a call your code made to create the object. You can look at the call chain in Instruments and go back along the call chain until you get to your code - that is the place where you are causing the allocation. Now, for that allocation, look at your memory handling for that object: Do you release it some time later?
There are lots of ways you can fail to release memory property so it would be hard to guess which one you might be hitting. Ones I see when helping people include allocating an object and assigning it to an instance variable via a property with the retain attribute, something like this:
#property (retain) NSString* myString;
...
self.myString = [[NSString alloc] initWithString: #"foo"];
the alloc+init creates a retained object, the self.myString = increments the retain count again. If coded correctly, the dealloc method releases the property via one of:
[myString release];
or
self.myString = nil;
And that takes care of the retain added with the self.myString = but does NOT take care of the retain from creation. Solutions, ONE of the following:
myString = [[NSString alloc] initWithString: #"foo"]; // doesn't call the setter method so no assignment retain - but doesn't call the setter which might be bad if non-trivial setter.
self.myString = [[[NSString alloc] initWithString: #"foo"] autorelease];
autorelease releases the alloc+init retain.
Now of course this is a contrived example because you'd probably really use:
self.myString = [NSString stringWithString: #"foo"];
which is a class method returning an autoreleased string and avoids the problem. But the idea is to show a simple example to explain this type of issue.
There are many other ways to not release memory properly, but the advice to work your way back up the call-chain until you get to your code is the way to go look at where you are triggering the allocation of the memory and then you can figure out why you aren't releasing that properly.
Try to do some issues in u code:
1. Please avoid hide declaration like
NSString *string = [dictionary valueForKey:[dictionary2 valueForKey:#"something"]]
Correct code is:
NSString *key = [dictionary2 valueForKey:#"something"];
NSString *string = [dictionary valueForKey:key];
key = nil;
Try to avoid declaration with autorelease and local declaration like:
NSArray *array = [NSArray array];
If u do this, make sure that u have:
NSArray *array = [NSArray array];
.... some code where array is using;
array = nil;
Better idea is alloc and release immediately when u don't need object and put it to nil.
3. Check if u using correct setters. Probably better idea is avoid to using it, at my experience, deallocate class start leaks for getters and setters, which was using before.
If u post some part of u code, where u seen most leaks (instruments give u possibility to click on leaked objects and see volume of leaks in programming code) community can suggest more.

Why am i getting a EXC_BAD_ACCES

Hey. I have been working on a Twitter application and have been stuck on a EXC_ BAD_ ACCESS error for quite some time. I know that EXC_ BAD_ ACCESS is a memory issue but i cannot pinpoint where the problem is. Here is my code sample:
- (void)viewDidLoad {
[super viewDidLoad];
NSString *path = #"/Volumes/Schools/BHS/Student/740827/Documents/Forrest McIntyre CS193P/Presence2";
NSArray *propList = [NSArray arrayWithContentsOfFile:[NSBundle pathForResource:#"TwitterUsers" ofType:#"plist" inDirectory:path]];
people = [[NSMutableArray alloc]init];
for (NSString *name in propList) {
Person *p = [[Person alloc] initWithUserName: name];
[people addObject: p];
[p release];
}
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
The exception is thrown on the last brace after the comment. I believe that it is truly thrown in the for loop somewhere but just shows up upon exiting.
Here is the implementation file for Person:
#implementation Person
#synthesize image;
#synthesize username;
#synthesize displayName;
#synthesize statusArray;
-(id)initWithUserName:(NSString *)userName {
if(self = [super init])
{
self.username = userName;
NSDictionary *info = [TwitterHelper fetchInfoForUsername:userName];
self.displayName = [info objectForKey:#"name"];
NSLog([NSString stringWithFormat:#"%#",[info objectForKey:#"profile_image_url"]]);
NSString *imageURL2 = [NSString stringWithFormat:#"%#",[info objectForKey:#"profile_image_url"]];
self.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString: imageURL2]]];
[info release];
self.statusArray = [TwitterHelper fetchTimelineForUsername:userName];
}
return self;
}
#end
Thanks for any help
EDIT: Here is the header file for PersonListViewController (the class that contains the ViewDidLoad).
This is just to show you where people is coming from.
#interface PersonListViewController : UITableViewController {
NSMutableArray *people;
}
#end
since you never retain propList or path you shouldn't be releasing them.
You should, however, release people
For an overview of memory management, see the Memory Management Programming Guide
For quick fixes, try the static analyzer.
I think the problem is here:
[propList release];
Since you created propList using arrayWithContentsOfFile you don't need to release it - it will be automatically released. The autorelease is actually what's causing the error since it is trying to release something that you already released manually.
ETA: as cobbal mentioned, you also don't need to release path.
Debugging EXC_BAD_ACCESS is difficult to debug. This happens when a message is sent to an object that is already released. You need to find out what is causing this generic error by turning on NSZombiEnabled environment variable so the Objective-C environment will be able to 'track' a deallocated object. Using this, when you get the error you can determine where the error occurred by looking at the call stack. You won't know where it is released but at least it will get you close.
I don't have it setup here, but you may also be passing a pointer to the error which will cause the object to not persist as a zombie/dummy.
Bottom line, you need to make sure the variables you are meaning to release, that you retain them as necessary.
This Technical Q&A by Apple gives tips on Finding bugs with EXC_BAD_ACCESS.
For one, neither of these are necessary in your example:
[path release];
[propList release];
because:
path is a string literal (will always exist)
propList is autoreleased
For any EXC_BAD_ACCESS errors, you are usually trying to send a message to a released object. The BEST way to track these down is use NSZombieEnabled.
This works by never actually releasing an object, but by wrapping it up as a "zombie" and setting a flag inside it that says it normally would have been released. This way, if you try to access it again, it still know what it was before you made the error, and with this little bit of information, you can usually backtrack to see what the issue was.
It especially helps in background threads when the Debugger sometimes craps out on any useful information.
VERY IMPORTANT TO NOTE however, is that you need to 100% make sure this is only in your debug code and not your distribution code. Because nothing is ever released, your app will leak and leak and leak. To remind me to do this, I put this log in my appdelegate:
if(getenv("NSZombieEnabled") || getenv("NSAutoreleaseFreedObjectCheckEnabled"))
NSLog(#"NSZombieEnabled/NSAutoreleaseFreedObjectCheckEnabled enabled!");
If you need help finding the exact line, Do a Build-and-Debug (CMD-Y) instead of a Build-and-Run (CMD-R). When the app crashes, the debugger will show you exactly which line and in combination with NSZombieEnabled, you should be able to find out exactly why.
http://www.cocoadev.com/index.pl?NSZombieEnabled can be useful in tracking down EXC_BAD_ACCESS bugs. Instead of deallocating objects when they are released it puts them into a zombie state that raises an exception when they are subsequently accessed. Just be sure not to ever release code with this flag set, as it will leak memory like a sieve.
what is self.editButtonItem? I don't see it in your .h file
A couple of things.
In initWithUserName: you're getting info from a method that doesn't contain alloc/copy/create. Further, you don't explicitly retain it. Yet you release it. This is problematic assuming fetchInfoForUsername: autoreleases its result as expected according to the Cocoa Memory management rules.
Using property accessors in initializers is considered bad form since it can cause KVO notifications to be sent out for a half-baked instance.

NSMutableDictionary throws a doesNotRecognizeSelector to objectForKey?

I'm a total noob to iPhone programming, and I've run into an exception being thrown that I just can't wrap my head around.
Background: The error is happening in a custom subview, and occurs immediately upon loading the program. I'm getting an exception thrown in the overridden drawRect method. The code throwing the error follows:
- (void)drawRect:(CGRect)rect{
NSNumber *points = [NSNumber numberWithInt: star.numberOfPoints];
//HERE. Doesn't recognize selector?!
CGPathRef t = (CGPathRef)[starPaths objectForKey:points];
/*snip*/
starPaths is initialized in awakeFromNib as an NSMutableDictionary with capacity 1.
The exception that's getting thrown is -[NSObject doesNotRecognizeSelector:]
starPaths is declared in the header file for the view as
NSMutableDictionary *starPaths;
and is initialized as
- (void)awakeFromNib{
starPaths = [NSMutableDictionary dictionaryWithCapacity: 1];
}
Finally, I haven't been able to get to a point in the code where I successfully add elements to the dictionary, since the code to add an entry relies on receiving a nil response from the dictionary to know that the that specific entry needs to be built.
Any suggestions? Any other information I should provide?
Any help at all would be appreciated, I feel like I'm missing something obvious, but I've been bashing my head against this all day with no luck.
If you do not retain the starPaths variable or explicitly allocate it yourself with [[NSMutableDictionary alloc] initWithCapacity:1] then it will be automatically released on the next iteration of the run loop.
You need to do
starPaths = [[NSMutableDictionary dictionaryWithCapacity:1] retain];
Or
starPaths = [[NSMutableDictionary alloc] initWithCapacity:1];
Just make sure to release it when you no longer need it.
A few things to check:
Is starPaths declared as an NSMutableDictionary* instance variable? You mention that it is initialized. Did you use the dictionaryWithCapacity method (which returns an auto-release object) or initWithCapacity (which needs to be explicitly retained). To be safe, you may want to retain it and release it when done.
Double-check to make sure the header files are properly included so the declaration of starPaths is included in implementation files that make use of it.
Generally speaking if you're getting mystery errors it has to do with corrupted memory. Try putting a breakpoint on the first line of the drawRect method and do a "po starPaths" in the debugger console window to see what's in it and what type of object the runtime thinks it is.

Objective-C memory issue (iPhone)

I'm somewhat confused by the following behavior I'm seeing within an Xcode project compiled for the iPhone simulator (or device):
NSString *test = [[NSString alloc] initWithCString:"foo"];
NSLog(#"test retain count = %d", [test retainCount]); // prints 1
[test release];
NSLog(#"test retain count = %d", [test retainCount]); // also prints 1 instead of 0
However, any further attempts to access 'test' result in a crash of the Xcode enviroinment, whether it be another [test retainCount] NSLog statement or otherwise (even if only to check if test is equal to nil).
Thoughts? Compiled within a simple View based test project...code exists within project's applicationDidFinishLaunching method.
Clarification -- I know NOT to do the above in practice. This was just a test to see why in some debugging cases a retain count of 1 wasn't actually reflecting the real state of an object. Thanks for your responses. This was just a test stub to see why I was seeing certain behavior in a few cases. What I'm really trying to do is track down a very small memory leak (0.06MB) that is consistently being created whenever I destroy/recreate a custom view.
Retain counts are a debugging aid and can be misleading based on what Cocoa may be doing behind the scenes. This is particularly true with string literals where the data is permanently available and is never really deleted.
Concentrate of ensuring your code follows the Cocoa memory management rules with respect to object ownership. Where necessary, use Instruments to check for actual memory leaks.
You are calling retainCount on test after it has been released and possibly deallocated, so definitely the result is not reliable, not to mention you shouldn't be sending dealloced objects any messages.
Don't rely on retain counts...the framework may be retaining stuff on its own.
Instead, rely on the rules of memory management for objective-C. If you alloc, init, or new something, you own it and are responsible for releasing it. If you didn't, you aren't.
don't rely on the retain count; instead, make sure that you're balancing retains and releases. Here are some links on Cocoa (Touch) memory management:
http://iamleeg.blogspot.com/2008/12/cocoa-memory-management.html
When you send that release, the string is dealloced — it has no other owners retaining it. Sending further messages to it (including retainCount) is an error and won't return anything meaningful.
And as others have pointed out, don't rely on retain counts to be particularly useful. They're a piece of the picture you can look at, but they're often misleading. For example, autoreleases don't show up in the retain count, sometimes objects are retained extra times for caching purposes, etc. In this particular case, though, you're not sending the message to a valid object at all, so you're lucky to get back an answer rather than a crash.
You should read Apple's memory management guide if you're unclear on any of this. It's not too complicated and absolutely essential for programming Cocoa.
One trick to improve the code shown above would be to use the 'set to nil' convention.
EG:
NSString *test = [[NSString alloc] initWithCString:"foo"];
NSLog(#"test retain count = %d", [test retainCount]); // prints 1
[test release];
test = nil; // set to nil, as I have released the pointer, I should no longer use it.
NSLog(#"test retain count = %d, test foobarpro = %d", [test retainCount], [test foobarPro]); // will now print 0 and 0 - because any objective-c call on a nil object returns 0 (and will not crash)
By setting a pointer to zero after you release it, you future proof your code a little: Someone coming along later and editing code 10 lines below can accidentally use 'test' with likely no ill effects (or an obvious crash). I even set pointers to nil in dealloc calls, as some of the hardest debugging tasks happen when non - nil 'bad' pointers are used in destruction.
Also you can debug with zombies to look for cases where you use pointers like your invalid test.
--Tom