memory leak from method in analyzer - iphone

Im nearing the end of my first app and im trying to go through my errors and memory management and clean everything up.
In the analyzer in xcode the following line comes up as a "potential memory leak"
- (NSMutableArray *) convertStringToASCIIMutableArray:(NSString *)stringIn {
NSLog(#"call:convertStringToASCIIMutableArray");
NSMutableArray *arrayOut = [[NSMutableArray alloc] initWithCapacity:stringIn.length];
//NSMutableArray *arrayOut = [NSMutableArray arrayWithCapacity:stringIn.length];
for (int i=0; i<stringIn.length; i++) {
unichar ch = [stringIn characterAtIndex:i];
if (ch > 96 && ch<123) ch = ch - 32; // convert to all capitals
[arrayOut insertObject:[NSNumber numberWithChar:ch] atIndex:i];
}
return arrayOut;
}
But when i remove the alloc call (as in the commented line or with an autorelease) the app crashes. Anyone know whats going on here?

"arrayOut" isn't autoreleased if you are in a manual reference counting environment.
Change return arrayOut to return [arrayOut autorelease] and the array will be released at the end of the current runloop.

Not seeing anything here. The "potential leak" is there because you're creating the mutable array using alloc-init and not releasing it when the method completes. Since you are returning the object, I recommend you create an autorelease version.
Now for the app crashing when using an autorelease version, you'll have to look at where it is crashing and what you're doing leading up to the crash. Do you know about setting up a breakpoint when an exception is raised? If not, follow this and the code will stop at the line when the crash happens so you can debug it:
Press Command+6 in xcode
Click on the + button in lower left corner
Select Add exception breakpoint
Set options for All exceptions, on throw and hit Done.

Well I didnt really solve my problem per se, but I converted my project to compile with the new Automatic Reference Counting (ARC, thanks #Kurt Revis and #Byron for bringing this feature to my attention)
To convert my project I just went to edit>Refactor>Convert to Obj-C ARC. Then I had to conform to the new rules of memory management as prompted after doing the "check" presented in the refactoring window. Essentially I had to remove some releases in my dealloc method, but it came up with some alerts in the issue navigator to tell me what to do.
ARC seems to be great. More info available here:
Steffen Itterheim

Related

What's a reliable way to make an iOS app crash?

I want to test my app's crash reporting out in the field by deliberately having it crash when the user performs a particular action that a real user is unlikely to do accidentally.
But what's a good reliable way of making the app crash that doesn't create a warning at compile time?
Edit: Note that many seemingly obvious answers to this question result in exceptions that get caught by Cocoa and thus don't result in the app crashing.
in Objective-C use C directly to cause a bad access
strcpy(0, "bla");
Note: while this works on any system I know -- in a future version of the C runtime OR the compiler this might not lead to a crash anymore. see Is null pointer dereference undefined behavior in Objective-C?)
(in swift you would have to bridge to objC to do this)
My current favourite:
assert(! "crashing on purpose to test <insert your reason here>");
A classic:
kill( getpid(), SIGABRT );
And some pr0n:
*(long*)0 = 0xB16B00B5;
All of them generate crashes captured by my crash reporting tool.
Since we all use Clang for iOS, this is fairly reliable:
__builtin_trap();
This has the benefit that it's designed for exactly this purpose, so it shouldn't generate any compiler warnings or errors.
How about a good old stack overflow :)
- (void)stackOverflow
{
[self stackOverflow];
}
abort(); causes abnormal termination… That is a crash.
Most popular one - unrecognised selector crash:
NSObject *object = [[NSObject alloc] init];
[object performSelector:#selector(asfd)];
Make sure you don't have -asdf method implemented in that class haha
Or index beyond bound exception:
NSArray * array = [NSArray array];
[array objectAtIndex:5];
And of course
kill( getpid(), SIGABRT );
I think in Swift you could easily throw a fatal error:
func foo() {
fatalError("crash!")
}
It is actually even intended to use this feature in case something goes wrong in order to make the app crash.
To avoid an if statement in a special case, you could use precondition, too. It's similar to assert, makes thus the intention (if wanted) pretty clear and is not removed in the final release as assert. It is used like precondition(myBoolean, "This is a helpful error message for debugging.").
Send a message to a deallocated object
exit(0);
(must... type... 30 characters)
You can also raise an exception:
[NSException raise:NSInternalInconsistencyException
format:#"I want to test app crashes!."];
Add a gesture recognizer to a view that recognizes a 10 finger tap (5 fingers for iPhone as 10 can get a bit crowded). The GR has a method attached to it that executes anyone of the previously mentioned surefire ways to get your app to crash. Most users are not going to lay 10 fingers down on your app, so you're safe from the general user accidentally causing the crash.
However you should be able to use something like Testflight or just deploying it to personal devices and test in the wild before ever submitting it to Apple. Having a forced crash could get your app rejected by Apple.
could try something like
NSArray* crashingArray = [NSArray arrayWithCapacity:1];
[crashingArray release];
should crash on an EXC_BAD_ACCESS (might need to release it a second time but normaly it should crash like this already)
I will go with:int raise(int sig);
To get more info >man raise
I would just kill the process normally:
kill(getpid(), SIGKILL);
So if you install a handler with signal you can also handle the crash, finishing to write opened files and these things.
I use
[self doesNotRecognizeSelector:_cmd];
When working with RubyMotion I use this:
n=Pointer.new ('c', 1)
n[1000] ='h'
Try this:
- (IBAction)Button:(id)sender
{
NSArray *array = [NSArray new];
NSLog(#"%#",[array objectAtIndex:8]);
}
a wrong NSLog statement will do it
NSLog(#"%#",1);

Understanding Memory management with NSDictionary release

iOS memory management is still something weird to understand to me but that's the most interesting aspect to me also, so I'm asking for some help here with my code.
I try to instantiate a NSDictionary object, I use it then I'd like to release but I got an object released error, this is the code:
if ([jsonArray count] > 0) {
NSDictionary *commentDictionary = [[NSDictionary alloc]init];
int i;
for (i = 0; i < [jsonArray count]; i++) {
commentDictionary = [jsonArray objectAtIndex:i];
NSLog(#"debugging message here"]);
commentLabel.text = [commentDictionary objectForKey:#"commentText"];
//[commentDictionary retain];
}
//[commentDictionary release];
commentDictionary = nil;
NSLog(#"NSDictionary retainCount = %d",[commentDictionary retainCount]);
}
Nothing special, I fill a dictionary from an array, in my code you can see I tried to release but than I commented out because of the error.
Why I can't release the dictionary?
Besides what's the difference between setting NSDictionary to nil that returns zero in retainCount and release (that should make the count -1)?
I thank you very much in advance for any kind of help in this topic.
Fabrizio
Do not call retainCount
retainCount is a horrible method to use for trying to figure out memory management. The absolute retain count of an object is rarely interesting and often will be unfathomable due to implementation details.
Read the documentation. It is pretty straightforward.
Now, to your code.
the alloc/init assignment to commentDictionary in your first line isn't needed and that object will be leaked on the assignment that is the first line in the for() loop.
instead of using for(;;), you could use for(commentDictionary in jsonArray) {...}
there is no reason to retain or release commentDictionary in that code; the object retrieved from the array will remain valid throughout the scope of that method.
Objective-C is a "nil eats messages" language. When you call a method on nil, that call will return 0 in almost all cases.
Oh, and what Cyril said. The static analyzer is a wonderful tool!
I advise you to run the static analyzer on that code: a lot of the mistakes that I do regarding memory management are explained by following the steps that the little blue arrows
describe.
That's a very useful/cool/forgiving tool to discover your own mistakes and understand what's happening. Build and Analyze in the build menu.
PS: retainCount is often wrong;

memory leak when adding objects to nsarray

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...)

unrecognized selector sent to instance 0x5d18d60... i'm going crazy!

I'm going crazy with this my little app... Please help me!!!
this is the source code of the app: Smoking.zip
It only saves a .dat file with an NSMutableArray.
Now, the first time you will launch the app, try to click the cigarette button sometimes: Everything should working fine.
Ok, now close the app, re-open it, and click again on the button. This time the app will crash with the "unrecognized selector sent to instance 0x5d18d60" error.
I was sure the problem was in saving the data, because when i commented the line "[theData writeToFile:dataFilePath atomically:YES];" in the "saveData" method the error disappeared.
Later i discovered that it appears again if i try to read the data from the NSMutableArray.
Please take a moment to check my project and help me, beacause i'm going crazy about that!!
You crazy man, it took quite some time to find these lines:
Cig *oldCig = [mainDelegate.smokeArray lastObject];
...
[oldCig release];
Why are you doing that? You effectively reduce the retain count of the last object in the array to 0. When saving, it is happily saved, with its retain count of zero.
On de-serialization, the decoder will retain any (sub) element it decodes, so the retain count of this last object will, for a brief moment, be 1. Then, on release of the decoder, it releases all elements, and poof goes the last Cig object. When getting that object from the array, you get a pointer pointing to something totally different, and the app crashes.
You should read up on memory handling. lastObject just returns a pointer to an object in the array, without retaining it for you, so you don't have to release it. Furthermore, for functions like
- (NSArray *) quando
try to return an autoreleased array, by calling autorelease before returning:
NSArray *newArray = [[[NSArray alloc] initWithObjects:year,...,nil] autorelease];
Then your other code doesn't have to think about releasing it. And release the dateFormatter. Anything you alloc, retain, copy or new, you must release or autorelease!
easy. On SDK 3.2 and 4.0 you need to make your button functions like this.
// Note it takes one argument UIButton.
- (IBAction) smoke:(UIButton * ) button {
Change this in your .h file and .m file, you dont haven to change anything else. Worked for me.

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.