Just when I thought I had gotten the hang of Objective-C's memory-management this error strikes me from nowhere...
Please consider the following code:
#implementation JglpNewsEntryParser
- (JglpNewsEntryParser *) initialize : (NSString *)content {
self = [super init];
if (self) {
currentHeader = nil;
currentText = nil;
currentDate = nil;
currentFullArticleUrl = nil;
entries = [[NSMutableArray alloc] init];
NSData *data = [content dataUsingEncoding: [NSString defaultCStringEncoding]];
NSXMLParser *parser = [[[NSXMLParser alloc] initWithData:data] autorelease];
[parser setDelegate: self];
[parser parse];
}
return self;
}
- (void)parser: (NSXMLParser *)parser didStartElement: (NSString *)elementName namespaceURI: (NSString *)namespaceURI qualifiedName: (NSString *)qName attributes: (NSDictionary *)attributeDict {
NSLog(#"Start!");
}
- (void)parser: (NSXMLParser *)parser didEndElement: (NSString *)elementName namespaceURI: (NSString *)namespaceURI qualifiedName: (NSString *)qName {
NSLog(#"End!");
}
- (void)parser: (NSXMLParser *)parser foundCharacters: (NSString *)content {
NSLog(#"Char!");
}
- (void)dealloc {
[super dealloc];
[entries release];
entries = nil;
}
The class is used in my unit test in the following manner:
- (void) testConstruct {
NSString *path = [[NSBundle bundleForClass:JglpNewsEntryParserTest.class] pathForResource: #"single-news-entry" ofType: #"html"];
NSError *error = nil;
NSString *data = [NSString stringWithContentsOfFile: path encoding: NSUTF8StringEncoding error: &error];
JglpNewsEntryParser *parser = [[[JglpNewsEntryParser alloc] initialize: data] autorelease];
STFail(#"");
}
After printing the "Start!", "End!" and "Char!" messages once, since the text XML contains only one entry, the test fails as it is supposed to at STFail. However, I do receive the following memory error message afterwards:
malloc: *** error for object 0xedf434: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
Constructing the data object in initialize seems set off the doomsday machine. If I uncomment it, the message disappears.
// ...
/*NSData *data = [content dataUsingEncoding: [NSString defaultCStringEncoding]];
NSXMLParser *parser = [[[NSXMLParser alloc] initWithData:data] autorelease];
[parser setDelegate: self];
[parser parse];*/
}
return self;
}
Am I missing something when constructing the NSData object from the NSString?
Thanks for any suggestions and best regards
KC
Dealloc should do [super dealloc] after releasing all the instance variables:
- (void)dealloc {
[entries release];
entries = nil;
[super dealloc];
}
because it is dealloc in NSObject that actually frees the memory used by the object. When you set entries to nil, you are writing to a block of memory that has been freed already.
Some other comments:
It seems wrong to me that you are doing work in -init. I would recommend that you have a method (call it, say, -parse) that actually invokes the parser. At the moment you are performing quite complex ooperations on an object that isn't guranteed to be fully initialised.
Also, it's best not to call your init method -initialize: to avoid confusion with +initialize which has a specific meaning in Cocoa. I'd call it -initWithContent:
Also, your instance variables are initialised to nil before -init, so lines like
ivar = nil;
are pointless in init and you also need to release all your instance variables in dealloc.
The [super dealloc] should be called at the end. Plus release objects which you allocate. An allocated object can be released by sending a message nil to it like entries = nil.
ATB.
Related
I'm using three20 to create image viewer. First i'm creating list of albums from the sql db and when user selects any album, its url string is passed to the this code that creates thums of available pics on network using XML Parser. everything works fine but when user goes back to the album list and selects another album. app crashes with 'Thread 1: Program received singal: "EXC+BAD_ACCESS" in main.m. plus XCode Product Analyze gives potential memory leak where i'm creating photoSource in the viewDidLoad. Here is the code
#import "AlbumController.h"
#import "PhotoSource.h"
#import "Photo.h"
#import "AlbumInfo.h"
#import "AlbumDatabase.h"
#implementation AlbumController
#synthesize albumName;
#synthesize urlAddress;
#synthesize images;
- (void)viewDidLoad
{
[super viewDidLoad];
// NSLog(#"%#", self.urlAddress);
[self createPhotos]; // method to set up the photos array
self.photoSource = [[PhotoSource alloc]
initWithType:PhotoSourceNormal
title:self.albumName
photos:images
photos2:nil];
self.navigationController.navigationBar.tintColor = [UIColor blackColor];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// release and set to nil
}
-(void)createPhotos
{
if ([stories count] == 0)
{
NSString *path = self.urlAddress;
[self parseXMLFileAtURL:path];
}
images = [NSMutableArray arrayWithCapacity:[stories count]]; // needs to be mutable
for (int i = 0; i < [stories count]; i++)
{
NSString *img = [[stories objectAtIndex:i] objectForKey:#"image"];
img = [img stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
//NSString * caption = [[stories objectAtIndex:i] objectForKey:#"caption"];
//caption = [caption stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
[images addObject:[[[Photo alloc] initWithURL:img smallURL:img size:CGSizeMake(320, 212)] autorelease]];
}
}
#pragma mark -
#pragma mark XML Parser Implementation
- (void)parserDidStartDocument:(NSXMLParser *)parser{
//NSLog(#"found file and started parsing");
}
- (void)parseXMLFileAtURL:(NSString *)URL
{
stories = [[NSMutableArray alloc] init];
//you must then convert the path to a proper NSURL or it won't work
NSURL *xmlURL = [NSURL URLWithString:URL];
// here, for some reason you have to use NSClassFromString when trying to alloc NSXMLParser, otherwise you will get an object not found error
// this may be necessary only for the toolchain
rssParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[rssParser setDelegate:self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[rssParser setShouldProcessNamespaces:NO];
[rssParser setShouldReportNamespacePrefixes:NO];
[rssParser setShouldResolveExternalEntities:NO];
[rssParser parse];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSString * errorString = [NSString stringWithFormat:#"Unfortunately it is not possible to load Pictures. Please check Internet Connection. (Error code %i )", [parseError code]];
//NSLog(#"error parsing XML: %#", errorString);
UIAlertView * errorAlert = [[UIAlertView alloc] initWithTitle:#"Failed to load the feed." message:errorString delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
[errorAlert release];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
//NSLog(#"found this element: %#", elementName);
currentElement = [elementName copy];
if ([elementName isEqualToString:#"item"]) {
// clear out our story item caches...
item = [[NSMutableDictionary alloc] init];
currentCaption = [[NSMutableString alloc] init];
//currentThumbnail = [[NSMutableString alloc] init];
currentImage = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
//NSLog(#"ended element: %#", elementName);
if ([elementName isEqualToString:#"item"]) {
// save values to an item, then store that item into the array...
//[item setObject:currentThumbnail forKey:#"thumbnail"];
//[item setObject:currentCaption forKey:#"caption"];
[item setObject:currentImage forKey:#"image"];
[stories addObject:[[item copy] autorelease]];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
// save the characters for the current item...
if ([currentElement isEqualToString:#"thumbnail"]) {
//[currentThumbnail appendString:string];
}// else if ([currentElement isEqualToString:#"caption"]) {
//[currentCaption appendString:string];
//}
else if ([currentElement isEqualToString:#"image"]) {
[currentImage appendString:string];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
NSLog(#"all done!");
NSLog(#"stories array has %d items", [stories count]);
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
if (toInterfaceOrientation == UIInterfaceOrientationPortrait ||
toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
return YES;
}
else
{
return NO;
}
}
#pragma mark -
#pragma mark Memory Management
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (void)dealloc {
[currentElement release];
[rssParser release];
[stories release];
[item release];
[currentCaption release];
//[currentThumbnail release];
[currentImage release];
[images release];
[stories release];
[super dealloc];
}
#end
and here is the didSelectRowAtIndexPath thats pushing this view
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
AlbumInfo *info = [_albumInfos objectAtIndex:indexPath.row];
AlbumController *albumController = [[AlbumController alloc] init];
albumController.urlAddress = info.address;
albumController.albumName = info.name;
[self.navigationController pushViewController:albumController animated:YES];
[albumController release];
}
Here is the code for AlbumController.h
#import <Foundation/Foundation.h>
#import "Three20/Three20.h"
#interface AlbumController : TTThumbsViewController <NSXMLParserDelegate>
{
NSString *albumName;
NSString *urlAddress;
// images
NSMutableArray *images;
// parser
NSXMLParser * rssParser;
NSMutableArray * stories;
NSMutableDictionary * item;
NSString * currentElement;
NSMutableString * currentImage;
NSMutableString * currentCaption;
}
#property (nonatomic, strong) NSString *albumName;
#property (nonatomic, strong) NSString *urlAddress;
#property (nonatomic, retain) NSMutableArray *images;
- (void)createPhotos;
- (void)parseXMLFileAtURL:(NSString *)URL;
#end
used this tutorial http://www.raywenderlich.com/1430/how-to-use-the-three20-photo-viewer
Need help solving this memory leak and need to know why its crashing.
Thanks
Simple. In Xcode 4.0+, Just click-hold on the Run icon, and press Profile. It'll open up Instruments, and you'll want Zombies. Then navigate your app to where the crash happened before, and this time, it'll show up in Instruments with the caller, and all the information about it.
The memory leak in viewDidLoad is caused by the following line:
self.photoSource = [[PhotoSource alloc]
initWithType:PhotoSourceNormal
title:self.albumName
photos:images
photos2:nil];
[PhotoSource alloc] returns an object you own (with a retain count of +1).
initWithType:title:photos:photos2: does not change the retain count.
So viewDidLoad is left with an object it owns, but no pointer to it. To balance the alloc you should send an autorelease message:
self.photoSource = [[[PhotoSource alloc]
initWithType:PhotoSourceNormal
title:self.albumName
photos:images
photos2:nil] autorelease];
I use a class member called "soapResults" when connecting to a webservice. I use a parser to parse the xml results (it is a json result inside the web service).
- (void) parser:(NSXMLParser *) parser
didStartElement:(NSString *) elementName
namespaceURI:(NSString *) namespaceURI
qualifiedName:(NSString *) qName
attributes:(NSDictionary *) attributeDict {
NSString *attName = [[NSString alloc]initWithFormat:#"%#Result",methodName];
if ([elementName isEqualToString:attName]) {
if (!soapResults) {
soapResults = [[NSMutableString alloc] init];
}
elementFound = YES;
}
[attName release];
}
Now soapResults is a retain member and released in dealloc. I tried to release this in the connection fail/pass but did not succeed. I also tried not to alloc it at all but then I get empty results.... Any help would be appriciated
Edit:
I also get memory leaks inside the parser:
-(void)parser:(NSXMLParser *) parser
foundCharacters:(NSString *)string {
if (elementFound) {
[soapResults appendString: string];//Memory leak here
}
}
If soapResults is a retain property you should change
soapResults = [[NSMutableString alloc] init];
To
self.soapResults = [NSMutableString string];
This releases the old value and retains the new one, avoiding leaks.
You alloc soapResults = [[NSMutableString alloc] init]; here but i don't see any release.
My app is working fine, but when I run instrument for checking for leaks, it shows me a leak at this line of code, in purple with a 100.0% mark:
xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
Here's the method containing this line:
-(NSString*) languageSelectedStringForKey:(NSString*) key
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"zh" ofType:#"lproj"];
if(selectedLanguage==French)
{
FinalString = [[NSString alloc] initWithFormat:#"http://www.xyz.com/api_com.php?page_id=%d",IDValue];
url = [[NSURL alloc] initWithString:FinalString];
}
else if(selectedLanguage==German)
{
FinalString = [[NSString alloc] initWithFormat:#"http://www.x.com/api_com.php?page_id=%d",IDValue];
url = [[NSURL alloc] initWithString:FinalString];
}
else if(selectedLanguage==Nepali)
{
FinalString = [[NSString alloc] initWithFormat:#"http://www.xy.com/api_com.php?page_id=%d",IDValue];
url = [[NSURL alloc] initWithString:FinalString];
}
xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[url release];
//Initialize the delegate.
parser = [[NewsParser alloc] initXMLParser];
//Set delegate
[xmlParser setDelegate:parser];
//Start parsing the XML file.
BOOL success = [xmlParser parse];
if(success)
NSLog(#"No Errors");
else
NSLog(#"Error Error Error!!!");
NSBundle* languageBundle = [NSBundle bundleWithPath:path];
NSString* str=[languageBundle localizedStringForKey:key value:#"" table:nil];
return str;
}
Here's my ViewDidLoad method from which languageSelectedStringForKey is called.
- (void)viewDidLoad
{
// Do any additional setup after loading the view from its nib.
appDelegate = (ProgAppDelegate *)[[UIApplication sharedApplication] delegate];
IDValue = 1;
textLabel.text=[self languageSelectedStringForKey:#"Welcome to Advance Localization"];
[super viewDidLoad];
}
What is causing this leak, and how can I fix it?
this is dealloc method:-
- (void)dealloc
{
[xmlParser release];
[parser release];
[nibLoadedCell release];
[super dealloc];
}
Do you ever call
[xmlParser release];
?
If not, you should release it when you no longer need it. Perhaps in the dealloc method of the same class in which that line appears.
You need to make NewsParser parser an instance variable and release it in the dealloc. Above, you init it, but you don't release it. Of course, you can't because it's a delegate of xmlParser. So, to make sure the object is retained, then properly released, it must be an ivar.
You never release FinalString (at least not in the code you posted)
this is held in the URL, which is held by the parser :)
Also, have you considered what would happen if this function is called twice?
Each time you say
xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
you would leak the previous xmlParser ;)
If you are allocating to an instance variable, you have to remember to release the previous object i.e.
[xmlParser release];
xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
Hi I am having issues with the following and it crashes the iPhone simulator, while the script has no errors it did bring up one warning in this script.
[c setImage:[attributeDict objectForKey:#"img"]];
The warning is
City may not respond to -setImage:
I am not sure what I have done wrong here is the fill source code.
#import "LocationsParser.h"
#implementation LocationsParser
#synthesize managedObjectContext;
-(id) initWithContext: (NSManagedObjectContext *) managedObjContext
{
self = [super init];
[self setManagedObjectContext:managedObjContext];
return self;
}
- (void)parseXMLFileAtURL:(NSURL *)URL parseError:(NSError **)error
{
// /Applications/MyExample.app/MyFile.xml
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:URL];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[parser setDelegate:self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
NSError *parseError = [parser parserError];
if (parseError && error) {
*error = parseError;
}
[parser release];
}
-(void) emptyDataContext
{
// Get all counties, It's the top level object and the reference cascade deletion downward
NSMutableArray* mutableFetchResults = [CoreDataHelper getObjectsFromContext:#"County" :#"Name" :NO :managedObjectContext];
// Delete all Counties
for (int i = 0; i
use this
[c setImage:(id)[attributeDict objectForKey:#"img"]];
c - what is it? E.g., if it is supposed to be UIImageView, you can do so:
[(UIImageView *)c setImage:[attributeDict objectForKey:#"img"]];
I have developed my iPhone application and now I am testing it with instruments to find memory leakages .
I have my appDelegate class in which I am fetching data from web service and then parse it then store it in an array..
Here is my applicationDidFinishLaunching method :
UIApplication* app = [UIApplication sharedApplication];
app.networkActivityIndicatorVisible = YES;
serviceURL = [[NSUserDefaults standardUserDefaults] stringForKey:#"readOnly_key"];
NSLog(#"text = %#",serviceURL);
if(serviceURL == nil)
{
//We set the default values from the settings bundle.
//Get the bundle path
NSString *bPath = [[NSBundle mainBundle] bundlePath];
NSString *settingsPath = [bPath stringByAppendingPathComponent:#"Settings.bundle"];
NSString *plistFile = [settingsPath stringByAppendingPathComponent:#"Root.plist"];
NSDictionary *settingsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistFile];
NSArray *preferencesArray = [settingsDictionary objectForKey:#"PreferenceSpecifiers"];
NSDictionary *item;
NSString *textEntry_Key;
NSString *readOnly_Key;
for(item in preferencesArray)
{
//Get the key of the item.
NSString *keyValue = [item objectForKey:#"Key"];
//Get the default value specified in the plist file.
id defaultValue = [item objectForKey:#"DefaultValue"];
if([keyValue isEqualToString:#"textEntry_key"]){
textEntry_Key = defaultValue;
}
NSLog(#"default value = %#",defaultValue);
if([keyValue isEqualToString:#"readOnly_key"])
readOnly_Key = defaultValue;
}
//Now that we have all the default values.
//We will create it here.
NSDictionary *appPrerfs = [NSDictionary dictionaryWithObjectsAndKeys:
textEntry_Key, #"textEntry_key",
readOnly_Key, #"readOnly_key",
nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:appPrerfs];
[[NSUserDefaults standardUserDefaults] synchronize];
}
NSURL *url5 = [[NSURL alloc] initWithString:#"http://192.168.0.150/Nirmal/Service.asmx/searchParameterList"];
//NSURL *url5 = [[NSURL alloc] initWithString:[NSString stringWithFormat:#"%#/Service.asmx/searchParameterList"]];
NSMutableURLRequest* request6=[NSMutableURLRequest requestWithURL:url5];
[request6 setHTTPMethod:#"POST"];
[request6 setTimeoutInterval:10];
//NSURLResponse *response6=nil;
// NSError *err6=nil;
//NSData *data6=[[NSURLConnection sendSynchronousRequest:request6 returningResponse:&response6 error:&err6] retain];
data2=[NSURLConnection sendSynchronousRequest:request6 returningResponse:nil error:nil];
if(data2 == nil)
{
UIAlertView* alert = [[UIAlertView alloc]initWithTitle:#"Alert" message:#"The network is not available.\n Please check the Internet connection." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
}
else
{
NSXMLParser *xmlParser5 = [[NSXMLParser alloc] initWithData:data2];
//Initialize the delegate.
SearchParameterDataParser *searchParameterDataParser = [[SearchParameterDataParser alloc] initSearchParameterDataParser];
//Set delegate
[xmlParser5 setDelegate:searchParameterDataParser];
//Start parsing the XML file.
#try {
// **the leakage line**
BOOL success1 = [xmlParser5 parse];
if(success1)
NSLog(#"No Errors");
else
NSLog(#"Error Error Error!!!");
}
#catch (NSException * e) {
NSLog(#"Exception in parsing %# %#",[e name], [e reason]);
}
//[xmlParser5 release];
[searchParameterDataParser release];
//[xmlParser5 release]; //leakage
}
[data2 release];
and this is my parser class :
#import "SearchParameterDataParser.h"
#import "AppDelegate.h"
#import "SearchClass.h"
#implementation SearchParameterDataParser
-(SearchParameterDataParser *)initSearchParameterDataParser{
[super init];
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
return self;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
if([elementName isEqualToString:#"SearchClass"]) {
[appDelegate.tempArray release];
appDelegate.tempArray = [[NSMutableArray alloc] init];
appDelegate.aSearchClass = [[SearchClass alloc]init];
}
NSLog(#"Processing Element: %#", elementName);
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(!currentElementValue){
[currentElementValue release];
currentElementValue = [[NSMutableString alloc] initWithString:string];
}
else
[currentElementValue appendString:string];
NSLog(#"Processing Value: %#", currentElementValue);
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if([elementName isEqualToString:#"SearchClass"]){
[appDelegate.tempArray addObject:appDelegate.aSearchClass];
return;
}
else
[appDelegate.aSearchClass setValue:currentElementValue forKey:elementName];
// [currentElementValue release];
currentElementValue = nil;
}
- (void) dealloc {
[super dealloc];
[aSearchClass release];
[currentElementValue release];
//[self release];
}
#end
I have specified the leakage line in the code .
Can anyone tell me what is going wrong or how can I solve the memory leakage ???????
This is pretty messy, as a suggestion to clean things up a bit, try moving some memory management stuff to accessor methods - your code is littered with -release methods. ie,
instead of:-
if([elementName isEqualToString:#"SearchClass"]) {
[appDelegate.tempArray release];
appDelegate.tempArray = [[NSMutableArray alloc] init];
}
try:-
if([elementName isEqualToString:#"SearchClass"]) {
appDelegate.tempArray = [NSMutableArray array];
}
where
appDelegate has a method
- (void)setTempArray:(NSMutableArray *)value {
if(value!=tempArray){
[tempArray release];
tempArray = [value retain];
}
}
or you can use a #synthesized accessor method to save coding it yourself. As an aside tempArray is a terrible name for an instance variable. Also note that you are releasing the old tempArray each time you make a new one - when does the last instance get cleaned up?
This is still not great as your parser class should not access directly the instance variables in appDelegate. Vardiables tempArray and searchClass should be private, but that is off the point and more of a design issue.
Where does url5 get released?
What is this supposed to do?
if(!currentElementValue){
[currentElementValue release];
}
release currentElementValue if it doesn't exist?
[data2 release];
Did you retain data2?
- (void) dealloc {
[super dealloc];
[aSearchClass release];
[currentElementValue release];
//[self release];
}
i thought aSearchClass was an instance variable on appDelegate?
This might sound like nitpicking but it is much easier to track down leaks if make your code clear and you know exactly what is going on.
You need to release both url5 and xmlParser5, since you've allocated them using alloc.
You should also be wary of calling release on properties, since the standard setters created when you #synthesize an instance variable typically release the previous value of the variable before setting the new value.