0

TouchXMLライブラリを使用して、Objective-cでいくつかのXMLを解析しようとしています。XMLのルート要素には名前空間があるため、CXMLNodeオブジェクトのTouchXMLライブラリのメソッドを次のように使用しています。

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

私のコードはこのメソッドを使用してXPathクエリに一致するノードの束を選択し、ノードごとにさらにいくつかのXPathクエリを実行していくつかのプロパティを読み取ります。何らかの理由で、2番目のクエリセットは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に読み込んでから、次のコードを使用して各<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]];
}

このコードは問題なく実行されます。しかし、その場合、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ドキュメントが正常に解析されますが、約1秒後にアプリがpointer being freed was not allocatedバグでクラッシュします。これらの行をコメントアウトして、、などをダミー値に設定すると、すべて正常に機能します_name_description

また、XMLから名前空間を引き出し、名前空間を気にしないTouchXMLライブラリのメソッドを使用してみましたが、正常に機能します(ただし、XMLでXMLを編集できるという贅沢はありません。実世界のシナリオ)。

私は、長くて複雑な質問を知っています。おそらく他の考えられる原因がたくさんありますが、問題をこれらの半ダースの行に確実に切り分けました。

4

1 に答える 1

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;
    }
}
于 2013-09-24T08:00:29.413 に答える