Memory crash using [CXMLNode nodesForXPath] with namespace mappings

南楼画角 提交于 2019-12-13 07:24:21

问题


I'm trying to parse some XML in objective-c, using the TouchXML library. The XML has a namespace in the root element, so I'm using the method in the TouchXML library on the CXMLNode object like:

- (NSArray *)nodesForXPath:(NSString *)xpath namespaceMappings:(NSDictionary *)inNamespaceMappings error:(NSError **)error;

My code uses this method to select a bunch of nodes matching an XPath query, then for each node I do some more XPath queries to read a few properties. For some reason, the second set of queries results in a pointer being freed was not allocated bug - I can't work out where this is coming from.

OK, here's a snippet of the XML:

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.2">
<Document>
  <Placemark>
    <name>Place 1</name>
    <description><![CDATA[6-20 Luck Street Eltham 3095]]></description>
    <Point>
      <coordinates>145.151138,-37.712663,0.000000</coordinates>
    </Point>
  </Placemark>
  <Placemark>
    <name>Place 2</name>
    <description><![CDATA[The Pines Shopping Centre, Reynolds Road Doncaster East 3109]]></description>
    <Point>
      <coordinates>145.168620,-37.762135,0.000000</coordinates>
    </Point>
  </Placemark>
    <Placemark>
        <name>Place 3</name>
        <description><![CDATA[25 Main Street Greensborough 3088]]></description>
        <Point>
            <coordinates>145.102788,-37.702511,0.000000</coordinates>
        </Point>
    </Placemark>
</Document>
</kml>

So I read this into a CMLXmlElement, then I've this code to read out each <Placemark> element:

_locations = [NSMutableArray array];
NSDictionary *mappings = [NSDictionary dictionaryWithObjectsAndKeys:@"http://earth.google.com/kml/2.2", @"kmlns", nil];
NSError *error = nil;
for (CXMLNode *node in [element nodesForXPath@"//kmlns:kml/kmlns:Document/kmlns:Placemark" namespaceMappings:mappings error:&error])
{
    [_locations addObject:[[ONEMapLocation alloc] initWithXmlElement:(CXMLElement *)node]];
}

This code runs without issue. But then, in that initWithXmlElement each of the location objects initialises itself like:

NSDictionary *namespaceMappings = [NSDictionary dictionaryWithObjectsAndKeys:@"http://earth.google.com/kml/2.2", @"kmlns", nil];
NSError *error = nil;
_name = ((CXMLNode*)[[xmlElement nodesForXPath:@"./kmlns:name/text()" namespaceMappings:namespaceMappings error:&error] lastObject]).stringValue;
_description = ((CXMLNode*)[[xmlElement nodesForXPath:@"./description/text()" namespaceMappings:namespaceMappings error:&error] lastObject]).stringValue;
NSString *rawCoordinates = _description = ((CXMLNode*)[[xmlElement nodesForXPath:@"./kmlns:Point/kmlns:coordinates/text()" namespaceMappings:namespaceMappings error:&error] lastObject]).stringValue;
NSArray *coordinateArray = [rawCoordinates componentsSeparatedByString:@","];
_latitude = [[coordinateArray objectAtIndex:1] floatValue];
_longitude = [[coordinateArray objectAtIndex:0] floatValue];

When this block of code runs, it successfully parses the XML document, but then about a second later the app crashes with the pointer being freed was not allocated bug. If I comment out those lines and set the _name, _description, etc. to dummy values it all works fine.

I've also tried pulling out the namespace from the XML and using the methods in the TouchXML library which don't bother with the namespace and it works fine (though I won't have the luxury of being able to edit the XML in the real world scenarios).

I know, long, complicated question, probably with a bunch of other possible causes, but I've definitely isolated the problem down to these half a dozen lines.


回答1:


Just in case somebody comes here with this or a similar problem - it sounds like the problem described here (https://github.com/TouchCode/TouchXML/issues/11), which I just happened to experience too. In essence it's a EXC_BAD_ACCESS error because the xml document is freed earlier than its child nodes, and when the child nodes want to dealloc themselves they crash.

I did not dig too deep into the TouchXML code, but the following changes to TouchXML seem to fix the problem and also do not lead to any memory leaks (I checked in Profiler):

In CXMLDocument.m:

-(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 {

        //### this is added ### fix for BAD_ACCESS on CXMLNode after releasing doc - get rid of all nodes in nodePool first to make sure they are released before the doc is released
        NSArray* allObjects = [nodePool allObjects];
        for(CXMLNode* child in allObjects)
        {
            [nodePool removeObject:child];
            [child invalidate];
        }
        //### until here ###

        nodePool = NULL;
    }
    //
    xmlFreeDoc((xmlDocPtr)_node);
    _node = NULL;
    //
}

In CXMLNode.h:

//### add manual dealloc function ###
-(void)invalidate; // added to prevent BAD_ACCESS on doc release ...

And in CXMLNode.m:

//### invalidate function added to be able to manually dealloc this node ###
-(void)invalidate {
    if (_node)
    {
        if (_node->_private == (__bridge void *)self)
            _node->_private = NULL;

        if (_freeNodeOnRelease)
        {
            xmlFreeNode(_node);
        }

        _node = NULL;
    }
}


来源:https://stackoverflow.com/questions/14875252/memory-crash-using-cxmlnode-nodesforxpath-with-namespace-mappings

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!