2013-02-14 60 views
0

我在使用TouchXML library來解析objective-c中的一些XML。該XML在根元素命名空間,所以我用像CXMLNode對象在TouchXML庫的方法:使用帶有名稱空間映射的[CXMLNode nodesForXPath]的內存崩潰

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

我的代碼使用這種方法來選擇一串匹配XPath查詢節點,那麼對於每個節點,我會執行一些更多的XPath查詢來讀取一些屬性。出於某種原因,第二組查詢導致了一個pointer being freed was not allocated錯誤 - 我無法確定這是從哪裏來的。

OK,這裏的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這一點,那麼我有這個代碼讀出每個<標>元素:

_locations = [NSMutableArray array]; 
NSDictionary *mappings = [NSDictionary dictionaryWithObjectsAndKeys:@"http://earth.google.com/kml/2.2", @"kmlns", nil]; 
NSError *error = nil; 
for (CXMLNode *node in [element [email protected]"//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)。

我知道,很長,很複雜的問題,可能還有其他一些可能的原因,但我確實已經將問題隔離到了這些六條線上。

+1

我其實已經在處理與命名空間選擇節點的TouchXML方法追查這一個錯誤 - 我有一個粗略的修復,我會很快發佈。下面的評論提醒我,如果你想要的答案。 – 2013-02-25 11:19:27

+0

我自己現在就發生了這個問題 - 你的修復工作? – Ertebolle 2013-03-12 07:40:25

回答

2

以防萬一有人來到這裏或類似的問題 - 這聽起來像這裏描述的問題(https://github.com/TouchCode/TouchXML/issues/11),我剛碰到這個問題。從本質上講,這是一個EXC_BAD_ACCESS錯誤,因爲xml文檔早於其子節點被釋放,並且當子節點想要釋放它們自己時,它們會崩潰。

我沒有挖太深進入TouchXML代碼,但TouchXML以下變化似乎來解決這個問題,也不會導致任何內存泄漏(我在探查選中):

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

是的!做得好。 – 2013-09-24 09:52:00

+0

注意:我剛剛發現類似的修復已經在TouchXML源代碼中完成,但無論出於何種原因,它尚未發佈......可能它不是真正開發出來的。 – TheEye 2013-09-24 10:08:45