Weird crash if I try to release CXMLDocument - objective-c

I am parsing some XML using TouchXML and I am getting a crash -EXC_BAD_ACCESS. What I found out through trial and error was that if I don't release my CXMLDocument (which I allocate), then everything is fine. Here's my code:
- (NSArray *)getLookUps {
//Do some stuff and then...
NSData *tempData = [NSURLConnection sendSynchronousRequest:request
returningResponse:nil
error:nil];
CXMLDocument *xmlDoc = [[CXMLDocument alloc] initWithData:tempData options:0 error:nil];
NSDictionary *mappings = [NSDictionary dictionaryWithObject:#"http://****/****"
forKey:#"****"];
NSLog(#"%#", [[NSString alloc] initWithData:tempData encoding:NSUTF8StringEncoding]);
NSArray *orders = [[xmlDoc rootElement] nodesForXPath:#"//****:Unit"
namespaceMappings:mappings
error:nil];
NSMutableArray *units = [NSMutableArray arrayWithCapacity:200];
for (CXMLElement *order in orders) {
NSArray *nodes = [order children];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:[nodes count]];
for (CXMLElement *node in nodes) {
[dictionary setObject:[node stringValue] forKey:[node name]];
}
[units addObject:dictionary];
}
//[xmlDoc release];
return units;
}
See on the 2nd last line, [xmlDoc release]. I have commented that out, because it crashes if I don't. What am I doing wrong? Thanks.

You probably need to retain your dictionary object otherwise it will also be released when you release the parser. Try changing [units addObject:dictionary]; to [units addObject:[dictionary retain]];.
Another idea is to set your xmlDoc pointer to autorelease:
CXMLDocument *xmlDoc = [[[CXMLDocument alloc] initWithData:tempData options:0 error:nil] autorelease];

This bug was reported and is flagged as fixed in the newer versions of the library.
http://code.google.com/p/touchcode/issues/detail?id=35
I haven't tested to see if it is actually fixed, a comment at that URL suggests that it isn't.
In my opinion, this library should be avoided altogether. For iOS apps, use libxml2 for several reasons:
It's tested and tried, through and through
It's fast and efficient
Building a node based representation of your XML might make it easier to code with, but it wastes memory as you always have the entire document in memory. You probably have it more than once while parsing. You should instead design your code to work with the libxml2 approach. You'll agree once you start parsing documents of substantial size.

I used TouchXML quite often, and (fortunately?) I did not have this problem up to now, but it just happened ...
I posted a solution here:
Memory crash using [CXMLNode nodesForXPath] with namespace mappings

I observed in TouchXML Class "CXMLDocument" we have the following handling in "dealloc" method.
- (void)dealloc
{
// Fix for #35 http://code.google.com/p/touchcode/issues/detail?id=35 -- clear up the node objects first (inside a pool so I _know_ they're cleared) and then freeing the document
#autoreleasepool {
nodePool = NULL;
}
//
xmlUnlinkNode(_node);
xmlFreeDoc((xmlDocPtr)_node);
_node = NULL;
}
I am not sure why we are using "autoreleasepool" in "dealloc". Is this is standard coding? Correct me if I am wrong.

Related

CoreData, child MOC on separate thread, unexpected: error: NULL _cd_rawData but the object is not being turned into a fault

Ok, im a bit lost with this one, i am currently trying to run a background core data operation using a second ManagedObjectContext with its type set to NSPrivateQueueConcurrencyType and failing miserably with the above error.
I have a custom subclass of NSOperation, which is being passed an NSArray of strings, and the PersistentStoreCoordinator from the main thread, it then creates its own ManagedObjectContext, runs a query and performs and operation.
Here is the code from the class:
//
// ProcessProfanity.m
// Hashtag Live Desktop
//
// Created by Gareth Jeanne on 24/03/2014.
// Copyright (c) 2014 Gareth Jeanne. All rights reserved.
//
#import "ProcessProfanity.h"
#import "Tweet.h"
static const int ImportBatchSize = 250;
#interface ProcessProfanity ()
#property (nonatomic, copy) NSArray* badWords;
#property (nonatomic, strong) NSManagedObjectContext* backgroundContext;
#property (nonatomic, strong) NSPersistentStoreCoordinator* persistentStoreCoordinator;
#end
#implementation ProcessProfanity
{
}
- (id)initWithStore:(NSPersistentStoreCoordinator*)store badWords:(NSArray*)words
{
self = [super init];
if(self) {
self.persistentStoreCoordinator = store;
self.badWords = words;
}
return self;
}
- (void)main
{
_backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
_backgroundContext.persistentStoreCoordinator = [self persistentStoreCoordinator];
_backgroundContext.undoManager = nil;
[_backgroundContext performBlockAndWait:^
{
[self import];
}];
}
- (void)import
{
//Create new fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
//Setup the Request
[request setEntity:[NSEntityDescription entityForName:#"Tweet" inManagedObjectContext:self.backgroundContext]];
NSError *error = nil;
//Create an array from the returned objects
NSArray* tweetsToProcess = [self.backgroundContext executeFetchRequest:request error:&error];
NSAssert2(tweetsToProcess != nil && error == nil, #"Error fetching events: %#\n%#", [error localizedDescription], [error userInfo]);
for (Tweet* tweetToCheck in tweetsToProcess){
__block NSString *result = nil;
[self.badWords indexOfObjectWithOptions:NSEnumerationConcurrent
passingTest:^(NSString *obj, NSUInteger idx, BOOL *stop)
{
if (tweetToCheck){
if ([tweetToCheck.text rangeOfString:obj].location != NSNotFound)
{
result = obj;
*stop = YES;
//return YES;
}
}
return NO;
}];
if (!result){
//DDLogVerbose(#"The post does not contain any of the words from the naughty list");
if(tweetToCheck){
tweetToCheck.profanity = [NSNumber numberWithBool:false];
}
}
else{
if(tweetToCheck){
//DDLogVerbose(#"The string contains '%#' from the the naughty list", result);
tweetToCheck.profanity = [NSNumber numberWithBool:true];
}
}
}
[self.backgroundContext save:NULL];
}
#end
And this is how i am calling it:
-(void)checkForProfanity{
if(!self.operationQueue){
self.operationQueue = [[NSOperationQueue alloc] init];
}
NSArray* termsToPass = [self.filterTerms copy];
ProcessProfanity* operation = [[ProcessProfanity alloc] initWithStore:self.persistentStoreCoordinator badWords:termsToPass];
[self.operationQueue addOperation:operation];
}
Edit 1
The specific line i seem to be getting the error on, or at least where Xcode is breaking is:
if ([tweetToCheck.text rangeOfString:obj].location != NSNotFound)
I have managed to narrow this down a bit, the NSArray that contains the list of terms to search the strings for is potentially quite large, possibly over a 1,000 NSStrings. If i test with an array of that size, i get the issue. However if i reduce the array to around 15 NSStrings, i do not get the error, so i don't think this is necessarily a thread related issue, i'm wondering if the array is getting released in the main thread. I have modified the code to make a deep copy, and then a __block copy as follows, but it doesn't seem to have helped.
self.badWords = [[NSArray alloc] initWithArray:words copyItems:YES];
and
for (Tweet* tweetToCheck in tweetsToProcess){
__block NSArray *array = [[NSArray alloc] initWithArray:self.badWords copyItems:YES];
__block NSString *result = nil;
[array indexOfObjectWithOptions:NSEnumerationConcurrent
In fact, at the point where Xcode breaks, if i PO array, i get an object not found message, but if i po result, i correct get an object returned that is nil.
Edit 2
So i have made the following changes, with no change:
Made the NSArray strong rather than copy:
#property (nonatomic, strong) NSArray* badWords;
And made it a copy when allocated:
self.badWords = [[NSArray alloc] initWithArray:words copyItems:YES];
And created a local copy of the NSArray with the ___block declaration inside the actual method processing the objects:
__block NSArray *array = [[NSArray alloc] initWithArray:self.badWords copyItems:YES];
Which should surely mean it sticks around for the life of the ProcessProfanity object?
Am i wrong in expecting to be able to PO the array from the breakpoint within the block?
In this instance the error message "error: NULL _cd_rawData but the object is not being turned into a fault" indicates that you are accessing a managed object outside of its context. Basically your fetch returns all the Tweets from your persistent store as faults. Once you try and access a property on the Managed Object, Core Data will fire a fault and fetch the full object from the store.
By calling the NSArray method indexOfObjectWithOptions:passingTest: with an option of NSEnumerationConcurrent you are implying that you want to perform asynchronous execution on the elements in your array. The keyword concurrent indicates that multiple threads can be used to operate on the array elements.
In your context this means that accessing a managed object inside this block might result in accessing it on a different thread from the managed object context that owns the object. So when you access tweetToCheck.text in your conditional check - if ([tweetToCheck.text rangeOfString:obj].location != NSNotFound), under the hood Core Data is fetching that managed object from the persistent store and returning it to a thread that is not part of the managed object contexts thread.
Furthermore, it is not necessary to use the method indexOfObjectWithOptions:passingTest: since you are not actually interested in the result of this operation.
It seems to me that it might be more convenient for you to use an NSSet as you are only testing to see whether or not a given tweet word exists in your profane words. Quoting the documentation for NSSet: "You can use sets as an alternative to arrays when the order of elements isn’t important and performance in testing whether an object is contained in the set is a consideration". Clearly this seems to meet your criteria.
So your init would look like:
-(id)initWithStore:(NSPersistentStoreCoordinator*)store
badWords:(NSSet*)badWords
{
self = [super init];
if(self) {
self.persistentStoreCoordinator = store;
self.badWords = [words copy];
}
return self;
}
Since you are only interested in updating tweets that have not yet been tagged for profanity you would probably only want to fetch tweets that haven't been flagged profane:
//Create new fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
//Setup the Request
[request setEntity:[NSEntityDescription entityForName:#"Tweet" inManagedObjectContext:self.backgroundContext]];
[request setPredicate:[NSPredicate predicateWithFormat:#"profanity = NO"]];
Now that you have an array of tweets that are not profane you could iterate through your tweets and check each word if it contains a profane word. The only thing you will need to deal with is how to separate your tweet into words (ignoring commas and exclamation marks etc). Then for each word you are going to need to strip it of diacritics and probably ignore the case. So you would end up with someone along the lines of:
if([self.badWords containsObject:badWordString]) {
currentTweet.profanity = [NSNumber numberWithBOOL:YES];
}
Remember, you can run predicates on an NSSet so you could actually perform a case and diacritic insensitive query:
NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:#"SELF = %#[cd]",wordToCheck];
BOOL foundABadWord = ([[[self.badWords filteredSetUsingPredicate:searchPredicate] allObjects] count] > 0);
Another thing you might want to consider is removing duplicate words in your tweets, you don't really want to perform the same check multiple times. So depending on how you find the performance you could place each word of your tweet into an NSSet and simply run the query on the unique words in your tweet:
if([[self.badWords intersectsSet:tweetDividedIntoWordsSet]) {
//we have a profane tweet here!
}
Which implementation you choose is up to you but assuming you are only using english in your app you are definitely going to want to run a case and diacritic insensitive search.
EDIT
One final thing to note is that no matter how much you try, people will always be the best means of detecting profane or abusive language. I encourage you to read this SO's post on detecting profanity - How do you implement a good profanity filter?
Ok, so still not quite sure what was going on, but i followed Daniels advice and re-wrote the indexOfObjectWithOptions method and now it's working. For completeness, and so it hopefully helps someone else, this is what i ended up doing.
DDLogInfo(#"Processing posts to check for bad language");
for (Tweet* tweetToCheck in tweetsToProcess){
__block NSArray *array = [[NSArray alloc] initWithArray:self.badWords copyItems:YES];
__block NSString *result = nil;
NSRange tmprange;
for(NSString *string in array) {
tmprange = [tweetToCheck.text rangeOfString:[NSString stringWithFormat:#" %# ", string]];
if (tmprange.location != NSNotFound) {
result = string;
DDLogVerbose(#"Naughty Word Found: %#", string);
break;
}
}
if (!result){
//DDLogVerbose(#"The post does not contain any of the words from the naughty list");
if(tweetToCheck){
tweetToCheck.profanity = [NSNumber numberWithBool:false];
}
}
else{
if(tweetToCheck){
//DDLogVerbose(#"The string contains '%#' from the the naughty list", result);
tweetToCheck.profanity = [NSNumber numberWithBool:true];
}
}

EXC_BAD_ACCESS memory error under ARC

In the method below I'm receiving "EXC_BAD_ACCESS" on the line containing the "urlString" variable. My research suggests that this error occurs when the program sends a message to a variable that has already been released. However since I'm using ARC I'm not manually releasing memory. How can I prevent ARC from releasing this variable too soon?
-(NSMutableArray *)fetchImages:(NSInteger *)count {
//prepare URL request
NSString *urlString = [NSString stringWithFormat:#"http://foo.example.com/image?quantity=%#", count];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
//Perform request and get JSON as a NSData object
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
//Parse the retrieved JSON to an NSArray
NSError *jsonParsingError = nil;
NSArray *imageFileData = [NSJSONSerialization JSONObjectWithData:response options:0 error:&jsonParsingError];
//Create an Array to store image names
NSMutableArray *imageFileNameArray;
//Iterate through the data
for(int i=0; i<[imageFileData count];i++)
{
[imageFileNameArray addObject:[imageFileData objectAtIndex:i]];
}
return imageFileNameArray;
}
Your problem has nothing to do with ARC. NSInteger isn't a class, so you don't want to be using the %# format. %# is going to send a description method to what the system thinks is an object, but when it turns out not to be one - CRASH. To solve your problem, you have two options:
You might want:
NSString *urlString =
[NSString stringWithFormat:#"http://foo.example.com/image?quantity=%d",
*count];
Make sure the count pointer is valid first!
You might need to change your method signature to be:
-(NSMutableArray *)fetchImages:(NSInteger)count;
and then change the urlString line as follows:
NSString *urlString =
[NSString stringWithFormat:#"http://foo.example.com/image?quantity=%d",
count];
You'll also need to fix all of the callers to match the new signature.
The second option seems more "normal" to me, but without more of your program it's impossible to be more specific.
you also may want to alloc and init the
NSMutableArray *imageFileNameArray;
before adding objects to it, otherwise you'll keep crashing. So you'd have
//Create an Array to store image names
NSMutableArray *imageFileNameArray = [[NSMutableArray alloc] init];

TouchJson memory leak?

I'm using TouchJson to parse json data from facebooks graph api. I'm getting some memory leaks though, and I don't really understand why...
In my effort to find the leak, I've removed everything else, so the following code is what I'm left with. The leak is one NSCFString for each loop, and I understand that it comes from the assignement to myItem.date, but I don't understand why?
I'm using the latest version of TouchJson
NSError *error;
NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserializeAsDictionary:data error:&error];
NSArray *jsonArray = [jsonDictionary objectForKey:#"data"];
for (NSDictionary *jsonEntry in jsonArray) {
NSDictionary *fromDictionary = [jsonEntry objectForKey:#"from"];
NSString *userId = [fromDictionary objectForKey:#"id"];
// Continue if it is a post from Atlas
if (userId != nil && [userId isEqualToString:#"10465958627"]){
MyItem *myItem = [[MyItem alloc] init];
// This uncommented causes the leak, why?
myItem.date = [jsonEntry objectForKey:#"created_time"];
[myItem release];
}
}
Thank you for your help!
Edit: I forgot to mention that MyItem is just an object with a property like so
#property (nonatomic, copy) NSString *date;

NSString EXC_BAD_ACCESS

I'm stuck with the following bit of code.
NSString *gridRef = [[NSString alloc] initWithFormat: #"%#", [converter LatLongToOSGrid: latLong]];
NSLog(#"Grid Ref: %#", gridRef);
self.answerLabel.text = [[NSString alloc] initWithFormat: #"%#", gridRef];
When I log gridRef, it displays the correct result. However, the line setting answerLabel.text causes an EXC_BAD_ACCESS error and the program crashes. IB is connected to the correct label, what is the problem?
Thanks
I've updated the code as follows:
- (IBAction)convertLatLong {
NSArray *latLong = [[NSArray alloc] initWithObjects: latTextField.text, longTextField.text, nil];
GridRefsConverter *converter = [[GridRefsConverter alloc] init];
NSString *gridRef = [[NSString alloc] initWithFormat: #"%#", [converter LatLongToOSGrid: latLong]];
NSLog(#"Grid Ref: %#", gridRef);
NSLog(#"Label: %#", self.answerLabel.text);
answerLabel.text = #"Yippy";
self.answerLabel.text = gridRef;
[gridRef release];
[converter release];
[latLong release];
}
answerLabel is initialised through #property #synthesize when the view controller is pushed onto the stack. (I don't know how it gets init'd apart from it's one of the magical things IB does for you. Or so I assume. I've used exactly the same method in other view controllers and have not had this issue.
I've found the culprits - the question is, how do I go about releasing them?
NSString *eString = [[NSString alloc] initWithFormat: #"%f", e];
NSString *nString = [[NSString alloc] initWithFormat: #"%f", n];
eString = [eString stringByPaddingToLength: (digits/2) withString: #"0" startingAtIndex: 0];
nString = [nString stringByPaddingToLength: (digits/2) withString: #"0" startingAtIndex: 0];
NSString *theGridRef = [letterPair stringByAppendingString: eString];
theGridRef = [theGridRef stringByAppendingString: nString];
[eString release];
[nString release];
return theGridRef;
and:
NSArray *gridRef = [[NSArray alloc] init];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithDouble: E]];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithDouble: N]];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithInteger: 8]];
NSString *theGridRef = [[NSString alloc] initWithFormat: #"%#", [self gridRefNumberToLetter: gridRef]];
[gridRef release];
[theGridRef autorelease];
return theGridRef;
}
You should enable zombie detection by setting the environment variable NSZombieEnabled to YES, so you can see which object causes the bad access (don't forget to remove this again when you found the bug).
Also you can use Instruments to find the location where the object actually gets released. For this start a new Instruments session and use the "Allocations" instrument. In the instrument settings check "Enable NSZombie detection" and "Record reference counts". When running the session you will break where the error occurs and you see a record of all retains/releases.
One place where you can have a quick look if your object is incorrectly freed is in the -viewDidUnload method, where you should release the outlet and set it to nil. If you forget the latter and you access the outlet somehow, it will result in a EXC_BAD_ACCESS.
Edited to match your update:
The problem is that you are assigning eString (and nString) a new string which was alloc/init-ed. Then you override those in the next statements, because -stringByPaddingToLength: (as well as all the other -stringBy... methods) return a new and autoreleased string object. So you lost the reference to the old string which means that there is a memory leak. Additionally at the end you release the already autoreleased objects explicitly which causes your bad access.
Instead you should create autoreleased strings from the beginning ([NSString stringWithFormat:...]) and don't release them at the end.
Check if asnwerLabel is actually non-null. You should also change this line:
self.answerLabel.text = [[NSString alloc] initWithFormat: #"%#", gridRef];
To:
self.answerLabel.text = [NSString stringWithFormat: #"%#", gridRef];
Otherwise, you will end up with a memory leak in that line.
Maybe the label is not inited at that point in your code, try to check it. Why are you allocating a new NSString?
Just do:
self.label.text = gridRef;
[gridRef release];
how is answerLabel created? You might need to retain that. Or you possibly need to release some things (gridRef)?
I can't see any other issues with your code.
You can (and probably should) set your
answerLabel.text = gridRef;
gridRef is already an NSString, so you don't need to alloc it again.
EXC_BAD_ACCESS is usually a memory thing related to your retain/release count not balancing (or in my extensive experience of it :p).
Okay, the problem was trying to release NSStrings, so I've stopped doing that and the problem has been solved.
Can someone clarify how strings are retained and released. I was of the impression that:
string = #"My String"; is autoreleased.
NSString *string = [[NSString alloc] init...]; is not autoreleased and needs to be done manually.

Is this the right way to create / destroy string in loop?

Just curious if this is the way to do this, just want to make sure its not leaking, although I would think I am only modifying the string contents.
NSMutableString *newPath = [[NSMutableString alloc] init];
for(fileName in [manager enumeratorAtPath:rootPath]){
if ([[fileName pathExtension] isEqual:#"exr"]) {
[fileArray addObject:fileName];
// THIS BIT
[newPath setString:rootPath];
[newPath appendString:#"/"];
[newPath appendString:fileName];
// HERE
attrDir = [manager attributesOfItemAtPath:newPath error:&myError];
fileSize = [attrDir objectForKey: #"NSFileSize"];
NSLog(#"File: /%# Size: %#", fileName, fileSize);
}
}
[newPath release];
gary
This looks fine leak-wise. If you're running Xcode 3.2 you can Build->Build & Analyzer to get Clang to check this sort of thing.
Remember you only have to release things you alloc, new, copy or retain.
Consider using stringByAppendingPathComponent, rather than hardcoding the #"/" path separator. NSString has a number of methods like this specifically for working with paths.
NSString* fullPath = [rootPath stringByAppendingPathComponent:fileName];
There's nothing wrong with it, although it could be better to use initWithFormat and release:
NSString *newPath = [[NSString alloc] initWithFormat:#"%#/%#",rootPath,fileName];
// do your thing
[newPath release];
There is absolutely nothing wrong with your code, it is correct memory management.
But it can be done with even less code and memory management needed:
for(fileName in [manager enumeratorAtPath:rootPath]){
if ([[fileName pathExtension] isEqualToString:#"exr"]) {
[fileArray addObject:fileName];
NSString* newPath = [rootPath stringByAppendingPathComponent:fileName];
attrDir = [manager attributesOfItemAtPath:newPath error:&myError];
fileSize = [attrDir objectForKey: #"NSFileSize"];
NSLog(#"File: /%# Size: %#", fileName, fileSize);
}
}