使用带有命名空间映射的 [CXMLNode nodesForXPath] 导致内存崩溃 [英] Memory crash using [CXMLNode nodesForXPath] with namespace mappings

查看:29
本文介绍了使用带有命名空间映射的 [CXMLNode nodesForXPath] 导致内存崩溃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 TouchXML 库在 Objective-c 中解析一些 XML.XML 在根元素中有一个命名空间,所以我在 CXMLNode 对象上使用 TouchXML 库中的方法,如:

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

我的代码使用这种方法来选择一组与 XPath 查询匹配的节点,然后对每个节点执行更多 XPath 查询以读取一些属性.出于某种原因,第二组查询导致 指针被释放未分配 错误 - 我不知道这是从哪里来的.

好的,这是 XML 的片段:

所以我把它读入一个 CMLXmlElement,然后我用这段代码读出每个 <Placemark>元素:

_locations = [NSMutableArray 数组];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]];}

此代码运行没有问题.但是,在那个 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];

当这段代码运行时,它成功地解析了 XML 文档,但大约一秒钟后应用程序崩溃了,指针被释放没有分配错误.如果我注释掉这些行并将 _name_description 等设置为虚拟值,则一切正常.

我还尝试从 XML 中提取命名空间并使用 TouchXML 库中的方法,这些方法不打扰命名空间并且它工作正常(尽管我无法编辑现实世界场景中的 XML).

我知道,这个问题很长很复杂,可能还有很多其他可能的原因,但我绝对已经将问题分解为这六行.

解决方案

以防万一有人带着这个或类似的问题来到这里 - 听起来就像这里描述的问题 (https://github.com/TouchCode/TouchXML/issues/11),我也碰巧遇到过.本质上,这是一个 EXC_BAD_ACCESS 错误,因为 xml 文档在其子节点之前被释放,并且当子节点想要释放自己时,它们会崩溃.

我没有深入研究 TouchXML 代码,但对 TouchXML 的以下更改似乎解决了问题并且也不会导致任何内存泄漏(我在 Profiler 中检查过):

CXMLDocument.m 中:

-(void)dealloc{//修复 #35 http://code.google.com/p/touchcode/issues/detail?id=35 -- 首先清除节点对象(在池中,所以我_知道_它们已被清除)然后释放文件@autoreleasepool {//###这是在发布文档后添加###修复CXMLNode上的BAD_ACCESS - 首先删除nodePool中的所有节点以确保它们在发布文档之前被释放NSArray* allObjects = [nodePool allObjects];for(CXMLNode* child in allObjects){[nodePool removeObject:child];[孩子无效];}//### 直到这里 ###节点池 = NULL;}//xmlFreeDoc((xmlDocPtr)_node);_node = NULL;//}

CXMLNode.h 中:

//###添加手动dealloc函数###-(无效)无效;//添加以防止文档发布时的 BAD_ACCESS ...

CXMLNode.m 中:

//### invalidate 函数添加到能够手动解除分配这个节点###-(无效)无效{如果(_节点){if (_node->_private == (__bridge void *)self)_node->_private = NULL;如果(_freeNodeOnRelease){xmlFreeNode(_node);}_node = NULL;}}

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.

解决方案

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;
    }
}

这篇关于使用带有命名空间映射的 [CXMLNode nodesForXPath] 导致内存崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆