objective-c – 使用NSXMLParser将XML解析为Core Data存储时的内存构建

我的应用程序有一个问题,它采用XML提要,解析它,并将结果存储到Core Data中.

问题仅发生在应用程序的第一次运行时,当商店中没有任何内容并且整个订阅源被解析和存储时.
问题只是内存分配累积起来,直到50%的尝试崩溃应用程序,通常大约10Mb.
分配的对象似乎是CFData(存储)对象,我似乎无法找到任何强制释放它们的方法.
如果你可以让它运行一次并成功完成解析并保存到核心数据,那么每次后续启动都没问题,内存使用量永远不会超过2.5Mb

这是我们进入代码之前的一般方法:
获取NSData对象的feed.使用NSFileManager将其存储为文件.
从文件路径创建URL并将其提供给parseXMLFile:方法.代表是自我.
在到达解析器时:didStartElement:namespaceURI:qualifiedName:attributes:我创建一个字典来捕获我需要的标签中的数据.
解析器:foundCharacters:方法将标记的内容保存到字符串
解析器:didEndElement:…方法然后确定标签是否是我们需要的东西.如果是这样的话,它会将它添加到字典中,否则它会忽略它.一旦它到达项目的末尾,它就会调用一种方法将其添加到核心数据存储中.

通过查看示例代码和其他人发布的帖子,似乎一般方法中没有任何错误.

代码如下,它来自视图控制器的更大上下文,但我省略了任何不直接相关的内容.

就我尝试的事情而言:
Feed是XML,没有转换为JSON的选项,抱歉.
这不是在共享文档区域中找到并存储的图像.

关于它可能是什么的线索:
这是我从乐器获得的最大数量最多的东西(每件256k):

Object Address Category Creation Time Live Size Responsible
Library Responsible Caller

1 0xd710000 CFData
(store) 16024774912 • 262144 CFNetwork URLConnectionClient::clientDidReceiveData(_CFData
const*,
URLConnectionClient::ClientConnectionEventQueue*)

这是代码,为内容的匿名编辑;)

-(void)parserDidStartDocument:(NSXMLParser *)feedParser { }

-(void)parserDidEndDocument:(NSXMLParser *)feedParser { }

-(void)parser:(NSXMLParser *)feedParser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
    eachElement = elementName;
    if ( [eachElement isEqualToString:@"item" ] )
    {
        //create a dictionary to store each item from the XML feed
        self.currentItem = [[[NSMutableDictionary alloc] init] autorelease];
    }
}


-(void)parser:(NSXMLParser *)feedParser foundCharacters:(NSString *)feedString 
{
    //Make sure that tag content doesn't line break unnecessarily
    if (self.currentString == nil) 
    {
        self.currentString = [[[NSMutableString alloc] init]autorelease];
    }
    [self.currentString appendString:feedString];

}

-(void)parser:(NSXMLParser *)feedParser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName 
{
    eachElement = elementName;
    NSString *tempString = [self.currentString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

if ( [eachElement isEqualToString:@"title"] ) 
    {
        //skip the intro title
        if (![tempString isEqualToString:@"Andy Panda UK"])
        {
            //add item to di citonary output to console
            [self.currentItem setValue:tempString forKey:eachElement];
        }
    }
    if ( [eachElement isEqualToString:@"link"] ) 
    {
        //skip the intro link
        if (![tempString isEqualToString:@"http://andypanda.co.uk/comic"])
        {
            //add item to dicitonary output to console
            [self.currentItem setValue:tempString forKey:eachElement];
        }
    }
    if ( [eachElement isEqualToString:@"pubDate"] ) 
    {
        //add item to dicitonary output to console
        [self.currentItem setValue:tempString forKey:eachElement];
    }
    if ( [eachElement isEqualToString:@"description"] ) 
    {
        if ([tempString length] > 150)
        {
            //trims the string to just give us the link to the image file
            NSRange firstPart = [tempString rangeOfString:@"src"];
            NSString *firstString = [tempString substringFromIndex:firstPart.location+5];
            NSString *secondString;
            NSString *separatorString = @"\"";
            NSScanner *aScanner = [NSScanner scannerWithString:firstString];
            [aScanner scanUpToString:separatorString intoString:&secondString];

            //trims the string further to give us just the credits
            NSRange secondPart = [firstString rangeOfString:@"title="];
            NSString *thirdString = [firstString substringFromIndex:secondPart.location+7];
            thirdString = [thirdString substringToIndex:[thirdString length] - 12];
            NSString *fourthString= [secondString substringFromIndex:35];

            //add the items to the dictionary and output to console
            [self.currentItem setValue:secondString forKey:@"imageURL"];
            [self.currentItem setValue:thirdString forKey:@"credits"];
            [self.currentItem setValue:fourthString forKey:@"imagePreviewURL"];
            //safety sake set unneeded objects to nil before scope rules kick in to release them
            firstString = nil;
            secondString = nil;
            thirdString = nil;
        }
        tempString = nil;
    }


    //close the feed and release all the little objects! Fly be free!
    if ( [eachElement isEqualToString:@"item" ] )
    {
        //get the date sorted
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        formatter.dateFormat = @"EEE, dd MMM yyyy HH:mm:ss ZZZZ";
        NSDate *pubDate = [formatter dateFromString:[currentItem valueForKey:@"pubDate"]];
        [formatter release];
        NSArray *fetchedArray = [[NSArray alloc] initWithArray:[fetchedResultsController fetchedObjects]];
        //build the string to make the image
        NSString *previewURL = [@"http://andypanda.co.uk/comic/comics-rss/" stringByAppendingString:[self.currentItem valueForKey:@"imagePreviewURL"]];

        if ([fetchedArray count] == 0)
        {
            [self sendToCoreDataStoreWithDate:pubDate andPreview:previewURL];
        } 
        else 
        {
            int i, matches = 0;
            for (i = 0; i < [fetchedArray count]; i++)
            {
                if ([[self.currentItem valueForKey:@"title"] isEqualToString:[[fetchedArray objectAtIndex:i] valueForKey:@"stripTitle"]])
                {
                    matches++;
                }
            }

            if (matches == 0)
            {
                [self sendToCoreDataStoreWithDate:pubDate andPreview:previewURL];
            }
        }
        previewURL = nil;
        [previewURL release];
        [fetchedArray release];
    }
    self.currentString = nil;
}

- (void)sendToCoreDataStoreWithDate:(NSDate*)pubDate andPreview:(NSString*)previewURL {
    NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
    NSEntityDescription *entity = [[fetchedResultsController fetchRequest] entity];
    newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
    [newManagedObject setValue:[NSDate date] forKey:@"timeStamp"];
    [newManagedObject setValue:[[currentItem valueForKey:@"title"] description] forKey:@"stripTitle"];
    [newManagedObject setValue:[[currentItem valueForKey:@"credits"] description] forKey:@"stripCredits"];
    [newManagedObject setValue:[[currentItem valueForKey:@"imageURL"] description] forKey:@"stripImgURL"];
    [newManagedObject setValue:[[currentItem valueForKey:@"link"] description] forKey:@"stripURL"];
    [newManagedObject setValue:pubDate forKey:@"stripPubDate"];
    [newManagedObject setValue:@"NO" forKey:@"stripBookmark"];
    //**THE NEW SYSTEM**
    NSString *destinationPath = [(AndyPadAppDelegate *)[[UIApplication sharedApplication] delegate] applicationDocumentsDirectory];
    NSString *guidPreview = [[NSProcessInfo processInfo] globallyUniqueString];
    NSString *guidImage = [[NSProcessInfo processInfo] globallyUniqueString];
    NSString *dpPreview = [destinationPath stringByAppendingPathComponent:guidPreview];
    NSString *dpImage = [destinationPath stringByAppendingPathComponent:guidImage];
    NSData *previewD = [NSData dataWithContentsOfURL:[NSURL URLWithString:previewURL]];
    NSData *imageD = [NSData dataWithContentsOfURL:[NSURL URLWithString:[currentItem valueForKey:@"imageURL"]]];
    //NSError *error = nil;
    [[NSFileManager defaultManager] createFileAtPath:dpPreview contents:previewD  attributes:nil];
    [[NSFileManager defaultManager] createFileAtPath:dpImage contents:imageD attributes:nil];
    //TODO: BETTER ERROR HANDLING WHEN COMPLETED APP
    [newManagedObject setValue:dpPreview forKey:@"stripLocalPreviewPath"];
    [newManagedObject setValue:dpImage forKey:@"stripLocalImagePath"];  
    [newManagedObject release];
    before++;
    [self.currentItem removeAllObjects];
    self.currentItem = nil;
    [self.currentItem release];
    [xmlParserPool drain];
    self.xmlParserPool = [[NSAutoreleasePool alloc] init];

}    

[编辑]
@Rog
我试过玩NSOperation但到目前为止没有运气,同样的我,ory用相同的物品建立,每个约256k.这是代码:

- (void)sendToCoreDataStoreWithDate:(NSDate*)pubDate andPreview:(NSString*)previewURL
{
    NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
    NSEntityDescription *entity = [[fetchedResultsController fetchRequest] entity];

    newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];


    [newManagedObject setValue:[NSDate date] forKey:@"timeStamp"];
    [newManagedObject setValue:[[currentItem valueForKey:@"title"] description] forKey:@"stripTitle"];
    [newManagedObject setValue:[[currentItem valueForKey:@"credits"] description] forKey:@"stripCredits"];
    [newManagedObject setValue:[[currentItem valueForKey:@"imageURL"] description] forKey:@"stripImgURL"];
    [newManagedObject setValue:[[currentItem valueForKey:@"link"] description] forKey:@"stripURL"];
    [newManagedObject setValue:pubDate forKey:@"stripPubDate"];
    [newManagedObject setValue:@"NO" forKey:@"stripBookmark"];

NSString *destinationPath = [(AndyPadAppDelegate *)[[UIApplication sharedApplication] delegate] applicationDocumentsDirectory];
NSString *guidPreview = [[NSProcessInfo processInfo] globallyUniqueString];
NSString *guidImage = [[NSProcessInfo processInfo] globallyUniqueString];
NSString *dpPreview = [destinationPath stringByAppendingPathComponent:guidPreview];
NSString *dpImage = [destinationPath stringByAppendingPathComponent:guidImage];

//Create an array and send the contents off to be dispatched to an operation queue
NSArray *previewArray = [NSArray arrayWithObjects:dpPreview, previewURL, nil];
NSOperation *prevOp = [self taskWithData:previewArray];
[prevOp start];

//Create an array and send the contents off to be dispatched to an operation queue
NSArray *imageArray = [NSArray arrayWithObjects:dpImage, [currentItem valueForKey:@"imageURL"], nil];
NSOperation *imagOp = [self taskWithData:imageArray];
[imagOp start];

[newManagedObject setValue:dpPreview forKey:@"stripLocalPreviewPath"];
[newManagedObject setValue:dpImage forKey:@"stripLocalImagePath"];  
//[newManagedObject release]; **recommended by stackoverflow answer
before++;
[self.currentItem removeAllObjects];
self.currentItem = nil;
[self.currentItem release];
[xmlParserPool drain];
self.xmlParserPool = [[NSAutoreleasePool alloc] init];


}

- (NSOperation*)taskWithData:(id)data
{
    NSInvocationOperation* theOp = [[[NSInvocationOperation alloc] initWithTarget:self
                                                                         selector:@selector(threadedImageDownload:) 
                                                                           object:data] autorelease];
    return theOp;
}

- (void)threadedImageDownload:(id)data 
{
    NSURL *urlFromArray1 = [NSURL URLWithString:[data objectAtIndex:1]];
    NSString *stringFromArray0 = [NSString stringWithString:[data objectAtIndex:0]];
    NSData *imageFile = [NSData dataWithContentsOfURL:urlFromArray1];
    [[NSFileManager defaultManager] createFileAtPath:stringFromArray0 
                                            contents:imageFile 
                                          attributes:nil];
    //-=====0jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjmn **OLEG**
}
最佳答案
看看AQXMLParser.有一个示例应用程序,显示如何在后台线程上将其与Core Data集成.它是一个流解析器,因此内存使用量很小.

设置第二个MOC并获取UI的结果控制器,并在解析完成后重新获取以获取新数据. (一种选择是在后台MOC保存以在上下文之间合并时注册更改通知)

对于图像,UIImageVew上有许多支持异步下载和占位符图像的第三方类别.因此,您将解析图像URL,将其存储在Core Data中,然后在查看该项目时在图像视图上设置URL.这样,用户无需等待下载所有图像.

转载注明原文:objective-c – 使用NSXMLParser将XML解析为Core Data存储时的内存构建 - 代码日志