【问题标题】:All objects overwritten by addObject: when using NSXMLParseraddObject 覆盖的所有对象:使用 NSXMLParser 时
【发布时间】:2013-10-04 10:30:27
【问题描述】:

我创建了一个名为tempArrayNSMutableArray,其中包含我想要的三个元素的字符。目前,当我设置断点并让解析器运行时,tempArray 每次都会正确更改,并为我提供所需的信息(纬度、经度、名称)。

然后,当解析器为“a-location”调用didEndElement... 方法时,我将tempArray 添加到名为parsedNearMe 的主NSMutableArray,它应该包含所有a-locations(每个都有-名称,纬度,经度)。

这一切都按预期工作。

问题: 每次我将tempArray 添加到parsedNearMe 时,它都会添加新对象,但会将所有现有对象设置为新对象的值。

我认为是什么问题 我认为问题在于,因为我将当前元素设置为tempArray,并且每次我覆盖parsedNearMe 中的每个对象,因为每个对象都是指向同一个tempArray 的指针。

我尝试或研究但没有成功的可能解决方案 - 以某种方式清除指针,以便我可以重用tempArray(或重新初始化它?) - 每次动态创建一个新名称来代替tempArray(不要认为在Objective-C中是可能的)

感谢您的任何建议,因为我已经在这方面花费了很多时间,我已经在做圈子了。此外,我希望当前架构能够正常工作(获取一个大型数组数组,因此我可以调用其中的 1 个数组并获取该注释所需的所有数据)。

我的代码如下:

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{

    if ( [elementName isEqualToString:@"a-location"]) 
    {
        if (!tempArray)
            tempArray = [[NSMutableArray alloc] init];
        return;
    }


    if ( [elementName isEqualToString:@"latitude"] ) 
    {
        latitude = [[NSMutableString alloc] init];
        return;
    } 


    if ( [elementName isEqualToString:@"longitude"] ) {
        longitude = [[NSMutableString alloc] init];
        return;
    }


    if ( [elementName isEqualToString:@"the-name"] ) {
        myName = [[NSMutableString alloc] init];
        return;
    }

}



-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    currentString = [[NSMutableString alloc]init];
    [currentString appendString:string];

}



- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{

    if ( [elementName isEqualToString:@"latitude"] ) {
        [tempArray addObject:currentString];
        return;
    }

    if ( [elementName isEqualToString:@"longitude"] ) {
        [tempArray addObject:currentString];
        return;
    }

    if ( [elementName isEqualToString:@"the-name"] ) {
        [tempArray addObject:currentString];
        return; 
    }

    if ( [elementName isEqualToString:@"a-location"])
    {
        [parsedNearMe setObject:tempArray forKey:[NSDate date]];
        [tempArray removeAllObjects];
    }
}

【问题讨论】:

    标签: ios objective-c nsmutablearray nsxmlparser


    【解决方案1】:

    有几个问题:

    1. a-locationdidEndElement 设置以日期为键的值,然后获取指向同一对象的指针并删除该对象中的所有项目。所以,而不是:

      if ( [elementName isEqualToString:@"a-location"])
      {
          [parsedNearMe setObject:tempArray forKey:[NSDate date]];
          [tempArray removeAllObjects];
      }
      

      您可以将其替换为:

      if ( [elementName isEqualToString:@"a-location"])
      {
          [parsedNearMe setObject:tempArray forKey:[NSDate date]];
          tempArray = nil;  // make sure you don't touch that object you just added to parsedNearMe
      }
      
    2. 我怀疑这个特定的 XML 是否会遇到这个问题,但是您的 foundCharacters 假定单个调用将返回所有数据。这不是一个有效的假设。有时需要多次调用。

    3. 您还拥有foundCharacters 为您不感兴趣的部分 XML 附加字符串。您应该确保currentStringnil,除非在解析纬度、经度和名称时。

    所以,至少,我建议如下:

    - (void)parserDidStartDocument:(NSXMLParser *)parser
    {
        parsedNearMe = [[NSMutableDictionary alloc] init]; 
        currentString = nil;
    }
    
    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
    {
        if ( [elementName isEqualToString:@"a-location"]) 
        {
            tempArray = [[NSMutableArray alloc] init];
        }
        else if ([elementName isEqualToString:@"latitude"] || 
                 [elementName isEqualToString:@"longitude"] || 
                 [elementName isEqualToString:@"the-name"]) 
        {
            currentString = [[NSMutableString alloc] init];
        }
    }
    
    -(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
    {
        [currentString appendString:string];
    }
    
    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
    {
        if ([elementName isEqualToString:@"latitude"] ||
            [elementName isEqualToString:@"longitude"] ||
            [elementName isEqualToString:@"the-name"] )
        {
            [tempArray addObject:currentString];
            currentString = nil;
        }
        else if ( [elementName isEqualToString:@"a-location"])
        {
            [parsedNearMe setObject:tempArray forKey:[NSDate date]];
            tempArray = nil;
        }
    }
    

    这反映了您当前的逻辑,创建以随机时间戳为键的字典,每个字典项的值是纬度、经度和名称的数组。

    我个人认为这个设计还有两个额外的缺陷:

    1. 您正在通过某个随机对象[NSDate date] 键入您的字典。那没有任何价值。你不妨把它做成一个数组。

    2. 您的纬度、经度和名称数组不明确。您的数据结构很大程度上取决于 XML 文件中项目的顺序。如果您要使用字典来处理任何事情,我会在这里使用它。

    所以,底线,而不是值是数组的字典,我会翻转它。我建议使用一组字典对象。我认为parsedNearMe 应该是NSMutableArray。而不是 NSMutableArray 称为 tempArray,您应该使用 NSMutableDictionary 称为 tempDictionary

    首先,定义几个 ivars:

    NSMutableArray *parsedNearMe;         // this was a NSMutableDictionary
    NSMutableDictionary *tempDictionary;
    

    然后您的 NSXMLParserDelegate 例程可能如下所示:

    - (void)parserDidStartDocument:(NSXMLParser *)parser
    {
        parsedNearMe = [[NSMutableArray alloc] init]; 
        currentString = nil;
    }
    
    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
    {
        if ( [elementName isEqualToString:@"a-location"]) 
        {
            tempDictionary = [[NSMutableDictionary alloc] init];
        }
        else if ([elementName isEqualToString:@"latitude"] || 
                 [elementName isEqualToString:@"longitude"] || 
                 [elementName isEqualToString:@"the-name"]) 
        {
            currentString = [[NSMutableString alloc] init];
        }
    }
    
    -(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
    {
        [currentString appendString:string];
    }
    
    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
    {
        if ([elementName isEqualToString:@"latitude"] ||
            [elementName isEqualToString:@"longitude"] ||
            [elementName isEqualToString:@"the-name"] )
        {
            [tempDictionary setObject:currentString forKey:elementName];
            currentString = nil;
        }
        else if ([elementName isEqualToString:@"a-location"])
        {
            [parsedNearMe addObject:tempDictionary];
            tempDictionary = nil;
        }
    }
    

    或者,如果您想将纬度和经度存储为 NSNumber 对象而不是字符串,您可以将 didEndElement 替换为:

    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
    {
        if ([elementName isEqualToString:@"latitude"] ||
            [elementName isEqualToString:@"longitude"])
        {
            [tempDictionary setObject:@([currentString doubleValue]) forKey:elementName];
            currentString = nil;
        }
        else if ([elementName isEqualToString:@"the-name"] )
        {
            [tempDictionary setObject:currentString forKey:elementName];
            currentString = nil;
        }
        else if ([elementName isEqualToString:@"a-location"])
        {
            [parsedNearMe addObject:tempDictionary];
            tempDictionary = nil;
        }
    }
    

    【讨论】:

    • 非常有条理的答案,谢谢。虽然我还没有尝试过你的解决方案,但我相信这会解决我的问题: tempArray = nil;我也同意你的设计建议。实际上,我最初只是将它作为两个 NSMutableArrays,但最终采用了当前的字典设计,因为我只是尝试了很多方法,并认为使用带有键的 NSDictionary 可能会解决每次覆盖对象的问题。
    • 另外,这也是为什么我试图将随机日期设置为关键。出于某种原因,我认为给它一个唯一的名称(日期)会阻止对象被覆盖。我还尝试从 0 开始计数器,并使用该数字作为键。感谢您解决这个问题:)。
    猜你喜欢
    • 2023-04-01
    • 2011-06-28
    • 2017-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多