简体   繁体   English

使用带有命名空间映射的[CXMLNode nodesForXPath]导致内存崩溃

[英]Memory crash using [CXMLNode nodesForXPath] with namespace mappings

I'm trying to parse some XML in objective-c, using the TouchXML library . 我正在尝试使用TouchXML库在objective-c中解析一些XML。 The XML has a namespace in the root element, so I'm using the method in the TouchXML library on the CXMLNode object like: XML在根元素中有一个命名空间,所以我在CXMLNode对象的TouchXML库中使用该方法,如:

- (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. 我的代码使用此方法选择一组与XPath查询匹配的节点,然后为每个节点执行一些更多的XPath查询以读取一些属性。 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. 由于某种原因,第二组查询导致pointer being freed was not allocated错误 - 我无法解决它来自何处。

OK, here's a snippet of the XML: 好的,这是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: 所以我把它读成CMLXmlElement,然后我用这段代码读出每个<Placemark>元素:

_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: 但是,在initWithXmlElement中,每个位置对象都自己初始化为:

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. 当这段代码运行时,它会成功解析XML文档,但是大约一秒钟之后,应用程序崩溃pointer being freed was not allocatedpointer being freed was not allocated错误。 If I comment out those lines and set the _name , _description , etc. to dummy values it all works fine. 如果我注释掉那些行并将_name_description等设置为虚拟值,那么一切正常。

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). 我也尝试从XML中取出命名空间并使用TouchXML库中的方法,这些方法没有使用命名空间,并且工作正常(尽管我没有能够编辑XML中的XML)。真实场景)。

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. 我知道,这是一个漫长而复杂的问题,可能还有很多其他可能的原因,但我肯定已将问题分解为这六个问题。

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. 以防万一有人来这里或类似的问题 - 这听起来像这里描述的问题( https://github.com/TouchCode/TouchXML/issues/11 ),我刚刚碰巧遇到过。 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. 本质上它是一个EXC_BAD_ACCESS错误,因为xml文档比其子节点更早被释放,并且当子节点想要解除它们自己时它们崩溃。

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): 我没有挖太深进入TouchXML代码,但TouchXML以下变化似乎来解决这个问题,也不会导致任何内存泄漏(我在探查选中):

In CXMLDocument.m : 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 : CXMLNode.h中

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

And in CXMLNode.m : 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;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM