我正在尝试使用TouchXML library在objective-c中解析一些XML。 XML在根元素中有一个命名空间,所以我在CXMLNode对象的TouchXML库中使用该方法,如:
- (NSArray *)nodesForXPath:(NSString *)xpath namespaceMappings:(NSDictionary *)inNamespaceMappings error:(NSError **)error;
我的代码使用此方法选择一组与XPath查询匹配的节点,然后为每个节点执行一些更多的XPath查询以读取一些属性。由于某种原因,第二组查询导致pointer being freed was not allocated
错误 - 我无法确定它来自何处。
好的,这是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>
所以我把它读成CMLXmlElement,然后我用这段代码读出每个&lt; Placemark&gt;元素:
_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]];
}
此代码运行没有问题。但是,在那个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文档,但是大约一秒钟之后,应用程序崩溃了pointer being freed was not allocated
错误。如果我注释掉这些行并将_name
,_description
等设置为虚拟值,则一切正常。
我也尝试从XML中取出命名空间并使用TouchXML库中的方法,这些方法没有使用命名空间,而且工作正常(尽管我没有足够的能力编辑现实世界中的XML场景。)
我知道,这是一个漫长而复杂的问题,可能还有很多其他可能的原因,但我确实把问题分解为这六个问题。
答案 0 :(得分:2)
以防万一有人来这里或类似的问题 - 这听起来像这里描述的问题(https://github.com/TouchCode/TouchXML/issues/11),我刚刚碰巧遇到过。本质上它是一个 EXC_BAD_ACCESS 错误,因为xml文档比其子节点更早被释放,并且当子节点想要解除它们自己时它们会崩溃。
我没有深入研究TouchXML代码,但 TouchXML 的以下更改似乎解决了问题,也没有导致任何内存泄漏(我在Profiler中检查过):
在 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;
//
}
在 CXMLNode.h :
中//### add manual dealloc function ###
-(void)invalidate; // added to prevent BAD_ACCESS on doc release ...
在 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;
}
}