I am using following code for showing a next view(tableview) in Iphone app.
It sometime works and some time app crashes without any exception log
NSLog(#"Ok"); is log everytime
50% attempts application crashes
-(IBAction)statusInitiationButAction:(id)sender{
#try {
NSArray *tempArrIniId = [eventInitiationArray valueForKey:#"act_id"];
int s;
if([tempArrIniId containsObject:selectedInitiateId]){
s=[tempArrIniId indexOfObject:selectedInitiateId];
}
[tempArrIniId release];
NSString *selStatusId = [NSString stringWithString:[[eventInitiationArray objectAtIndex:s] objectForKey:#"status_id"]];
for (int i=0; i<[statusInitiationArray count]; i++) {
id statusDict = [statusInitiationArray objectAtIndex:i];
if ([selStatusId intValue] == [[statusDict objectForKey:#"id"] intValue]) {
[statusDict setValue:#"1" forKey:#"selected"];
[statusInitiationArray replaceObjectAtIndex:i withObject:statusDict];
}else {
[statusDict setValue:#"0" forKey:#"selected"];
[statusInitiationArray replaceObjectAtIndex:i withObject:statusDict];
}
}
NSLog(#"statusInitiationTable...%#",statusInitiationArray);
[statusInitiationTable.tableView reloadData];
[[self navigationController] pushViewController:statusInitiationTable animated:YES];
NSLog(#"ok");
}#catch (NSException * e) {
NSLog(#"statusInitiationButAction....%#",e);
}
}
Can anybody guide me about the problem.
Thanks
Amit Battan
You should not be doing this:
[tempArrIniId release];
Because in this line...
NSArray *tempArrIniId = [eventInitiationArray valueForKey:#"act_id"];
...you are not creating the tempArrIniId you are merely obtaining a reference to it. Therefore, you did not retain it and have no need to release it.
You are getting an intermittent crash because your over releasing the object pointed to by tempArrIniId while that object is still a member of the eventInitiationArray. When the array tries to access the object or even count itself, it crashes because there is not an object where it expects one to be. That crash can happen anywhere in the app where the 'eventInitiationArray' is used.
Overzealous releasing causes more problems than it prevents. When in doubt, don't release. It's trivial to find memory leaks with the analysis tools if you don't release something you should have and it's trivial to fix it.
It's a lot harder to track down a crash caused by over releasing an object held by other objects such as arrays because the subsequent crash can occur far from where the over release occurred.
Related
I'm doing some work using the RedPark Serial Cable for iOS. This is a cable that allows serial communication between an iOS device and another device like a microcontroller.
There is a method that comes with the RedPark SDK that reads the bytes available on the Serial Line. This method is called anytime the cable receives informatino.
(void)readDataBytes(UInt32 bytes) {
NSString *s = [[NSString alloc] initWith...bytes];
NSString *editedString = [self extractSubStringMethod:s];
[myArray addObject:editedString];
}
The microcontroller is sending the information in the following format
<msg>xxxxxxxxxxxxx</msg>
I want to be able to extract the x's from the message (which is taken in as an NSString). At the moment I'm using NSRange to extract everything after position 4 (the first x) and before the final "< /msg>" I'm not convinced it works and was wondering is there any other ideas?
Finally, I have an NSThread which is running alongside this, I have the messages being added to an NSMutableArray. So what I want is, the NSThread to be manipulating/displaying the message information when there is a message received from the cable. I have something like the following
//Thread method,
while([myArray count] > 0) //Don't believe this is neccesary but its in anyway
{
for(int i = 0; i < [myArray count]; i++){
NSString *string = [myArray objectAt(i)];
[self displayString:string];
[myArray removeObjectAt(i);
}
}
I believe it's crashing around the above... [self displayString:string] just sets the value of a label, something like
-(void)displayString(NSString *string) {
label.text = [string charAt(1)];
}
The code above is just from memory as I've left my Mac at home and I'm in work.
Any suggestions/help would be appreciated
Instead of
while([myArray count] > 0) //Don't believe this is neccesary but its in anyway
{
for(int i = 0; i < [myArray count]; i++){
NSString *string = [myArray objectAt(i)];
[self displayString:string];
[myArray removeObjectAt(i);
}
}
try like this:
while ([myArray count] > 0)
{
[self displayString: myArray[0]];
[myArray removeObjectAtIndex: 0];
}
To address your first concern, [string substringWithRange:NSMakeRange(5, string.length - 6)] should be sufficient and should work without any issues. You could use an NSRegularExpression to match the content inside the tags, but that would be overkill for such a simple task (and would have a performance hit).
Secondly, UIKit methods must only be called from the main thread (see documentation):
Note: For the most part, UIKit classes should be used only from an application’s main thread. This is particularly true for classes derived from UIResponder or that involve manipulating your application’s user interface in any way.
You appear to be setting the text of a label in a different thread. This is what is causing the crash. You need to call your displayString: method like this:
[self performSelectorOnMainThread:#selector(displayString:) withObject:string waitUntilDone:YES];
This will execute the method on the main thread instead of on a background thread.
You must not update UI on secondary thread.
Mutable classes are not thread safe.
while([myArray count]){
NSString *string = [myArray objectAt(0)];
[self performSelectorOnMainThread:#selector(displayString:) withObject:string waitUntilDone:NO];
[myArray removeObjectAt(0);
}
I get leaks if I dont put it in dealloc. I get a crash EXC_BAD_ACCESS If I do. I cannot see anything wrong with this code. The bad access is pointed at [events release]. Have I made a mistake in the code below or is Instruments just having a laugh at my expense?
events is an NSArray
#interface EventsViewController : UITableViewController
{
#private
NSArray *events;
}
- (void)viewDidLoad
{
events = [[self getEvents] retain];
}
- (void)dealloc
{
[events release];
[super dealloc];
}
- (NSArray*)getEvents
{
NSMutableArray *response = [[[NSMutableArray alloc] init] autorelease];
//Some sql
while(sqlite3_step(statement) == SQLITE_ROW)
{
Event *event = [[[Event alloc] init] autorelease];
event.subject = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
[response addObject:event];
}
return response;
}
Update
A lot of you are saying the code is fine which is a plus. I dont manipulate events elsewhere - I have removed any code that does to try and single out the crash. Perhaps its in the parent view?
This is the click event that pushes the EventsViewController:
- (void)eventsClick:(id)sender
{
EventsViewController *eventsViewController = [[EventsViewController alloc] initWithNibName:#"EventsViewController" bundle:nil];
eventsViewController.anywhereConnection = anywhereConnection;
eventsViewController.contact = contact;
[[self navigationController] pushViewController:eventsViewController animated:YES];
[eventsViewController release];
}
The crash is actually happening when I return to the parent view. (I think it is considered a parent in this scenario). But perhaps the [eventsViewController release] just triggers dealloc in the EventViewController.
Have you considered just refactoring your code to use ARC? It works with iOS 4 and up and will make your life a lot easier. There are plenty of tutorials out there that will guide you how to do it, and will remove the need to manually figure out the nuances of memory management.
If your Events object has property 'subject' set as assign, then the results of stringWithUTF8String: will not be retained. (Same thing if Events is a C++ object.)
The stringWithUTF8String: method returns an auto-released object that will be released at the next turn of the event loop.
There is a huge difference when you reference a variable via "self", and when you don't.
When you use
events = [[self getEvents] retain];
the memory allocated in getEvents never gets stored in the class property and is basically a leak.
You need to use
self.events = [self getEvents]; // no need to use retain if property is correctly defined.
Then
[events release];
should work fine.
try putting
events = nil;
in dealloc.
Removed release statements. Some of them seemed to be okay, but that was probably just because other things were exploding first.
- (void)handleNowPlayingItemChanged:(id)notification {
MPMediaItem *item = self.musicPlayer.nowPlayingItem;
NSString *title = [item valueForProperty:MPMediaItemPropertyTitle];
NSNumber *duration = [item
valueForProperty:MPMediaItemPropertyPlaybackDuration];
float totalTime = [duration floatValue];
progressSlider.maximumValue = totalTime;
CGSize artworkImageViewSize = self.albumCover.bounds.size;
MPMediaItemArtwork *artwork = [item valueForProperty:
MPMediaItemPropertyArtwork];
if (artwork) {
self.albumCover.image = [artwork imageWithSize:artworkImageViewSize];
} else {
self.albumCover.image = nil;
}
titleLabel.text = title;
/*OpenEars stuff*/
}
In another question I mention the SQLite errors concerning artwork.
** Deleted error and details concerning NSZombieEnabled alert of call to released objects. **
Well don't I feel stupid. It was all memory management.
I put effort into not leaking anything, even in a temporary solution, and yet I did this...
In the code you provide I do not see any calls to retain, alloc/init, or some variation of copy. That means that you should not have a any calls to release in that method and that will be the cause of your crash. Make sure you are not over releasing in other methods and remember the basics of memory management.
You're releasing title and artwork, but they're not yours. This will lead, soon or later, to a tentative to release an already deallocated object (from item's dealloc or somewhere else).
// [artwork release];
//[title release];
comment those since those are autoreleased object
I am in memory-leak cleanup mode on my latest app, and have come across something that I am unable to solve.
the following method has been cleaned up except for 1 nagging issue. Instruments tells me that my NSMutableArray called itemsToKeep is leaking memory, at the point that I am creating the object. Any ideas on why I am leaking memory would be most appreciated.
Here are some notes on retainCounts:
entering the method: self.myList has retainCount = 1
exiting the method: self.myList has retainCount = 2 and itemsToKeep has retainCount= 2.
I can easily do a [itemsToKeep release] at the end which brings both counts down to 1, but the app crashes after a while (and I think I know why).
Does anyone know how I can get rid of the memory leak for itemsToKeep?
Thanks.
-(void)parsedScores:(BOOL)shouldAdd {
//trim space, tab, newline from both ends
NSString *tmp = self.lblCurrentName.text;
NSString *list = [self trimString:tmp];
NSString *separators = #",";
[self.myList removeAllObjects]; // doesn't impact retain counts
self.myList = (NSMutableArray *)[list componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:separators]]; //this bumps up the self.myList retain count to 2
NSMutableArray *itemsToKeep = [NSMutableArray arrayWithCapacity:30];
for (NSString *item in self.myList) {
NSString *tmpItem = [self trimString:item];
if (! [self shouldRemoveItem:tmpItem]) {
[itemsToKeep addObject:tmpItem];
}
}
self.myList = itemsToKeep; //makes both variables' retain counts = 2
}
I can't see a leak in the method you've provided, so I assume it's happening elsewhere. You should check if self.myList is retained somewhere else without it being released.
Also, you probably shouldn't be looking at the retain count for debugging purposes. The retain count can be misleading because it doesn't matter how many times an object is retained as long as it's released an equal amount of times.
Update: I edited the code, but the problem persists...
Hi everyone,
this is my first post here - I found this place a great ressource for solving many of my questions. Normally I try my best to fix anything on my own but this time I really have no idea what goes wrong, so I hope someone can help me out.
I am building an iPhone app that parses a couple of xml files using TouchXML. I have a class XMLParser, which takes care of downloading and parsing the results. I am getting memory leaks when I parse an xml file more than once with the same instance of XMLParser.
Here is one of the parsing snippets (just the relevant part):
for(int counter = 0; counter < [item childCount]; counter++) {
CXMLNode *child = [item childAtIndex:counter];
if([[child name] isEqualToString:#"PRODUCT"])
{
NSMutableDictionary *product = [[NSMutableDictionary alloc] init];
for(int j = 0; j < [child childCount]; j++) {
CXMLNode *grandchild = [child childAtIndex:j];
if([[grandchild stringValue] length] > 1) {
NSString *trimmedString = [[grandchild stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[product setObject:trimmedString forKey:[grandchild name]];
}
}
// Add product to current category array
switch (categoryId) {
case 0:
[self.mobil addObject: product];
break;
case 1:
[self.allgemein addObject: product];
break;
case 2:
[self.besitzeIch addObject: product];
break;
case 3:
[self.willIch addObject: product];
break;
default:
break;
}
[product release];
}
}
The first time, I parse the xml no leak shows up in instruments, the next time I do so, I got a lot of leaks (NSCFString / NSCFDictionary).
Instruments points me to this part inside CXMLNode.m, when I dig into a leaked object:
theStringValue = [NSString stringWithUTF8String:(const char *)theXMLString];
if ( _node->type != CXMLTextKind )
xmlFree(theXMLString);
}
return(theStringValue);
I really spent a long time and tried multiple approaches to fix this, but to no avail so far, maybe I am missing something essential?
Any help is highly appreciated, thank you!
The issue is likely in this line:
[self.mobil addObject:[product copy]];
By calling for a copy of product you're creating a new NSMutableDictionary instance with a retain count of 1. The mobil instance, however, will increment the copy's retain count when you send it the addObject: message, so the retain count of the copy is now 2. Generally speaking, an object is responsible for handling its own object memory, so any time you message setFoo: or addObject:, you can just pass the object directly, even if its autoreleased or you are planning to release it right after the call; it is the receiver's responsibility to retain the object you're passing if it needs to hold onto it.
Because you did not assign the copy to any variable, you do not have a pointer that you can use to decrement the copy's retain count now that you're no longer interested in it, so even if mobil releases the product copy at some point, the copy will never reach a retain count of 0. Your [product release] statement at the end of the for loop releases the original product object, not the copy you created.
Instead, try the following and see if instruments is happier:
[self.mobil addObject:product];
In simple terms, every time you use copy, you also have to use release/autorelease somewhere.
And in this instance, the even easier answer is to not use copy in the first place, since you aren't doing anything with the original version of product after you've copied it.
I fixed the problem myself. It was kind of stupid, but maybe someone might come across the same thing, so I am going to post it here.
1) I had mutable array set up as instance variables like this:
#interface XMLParser : NSObject {
// ...
NSMutableArray *mobil;
// ...
}
#property(nonatomic, retain) NSMutableArray *mobil;
#end
Everytime I wanted to restore new data inside I did:
self.mobil = nil;
Which did not what I wanted to do, so this is the better approach:
[self.mobil removeAllObjects];
2) The dealloc method has to be like this to fix the leaks (because mobil is defined as a property):
-(void)dealloc {
[mobil release];
self.mobil = nil;
}
Whew, that has been a lot of work to find out - hope it saves someone else some time :-)