使用NSInputStreamstream式传输NSXMLParser

更新:

当使用NSXMLParser类的方法initWithContentsOfURL ,而不是下载XML feed时parsing,它似乎尝试将整个XML文件加载到内存中,然后才启动parsing过程。 如果XML提要很大(使用过量的RAM,本质上是低效的,因为不是与下载并行parsing,只在下载完成后才开始parsing等),这是有问题的。

有没有人发现如何parsing,因为饲料正在使用NSXMLParserstream式传输到设备? 是的,您可以使用LibXML2 (如下所述),但似乎应该可以使用NSXMLParser来完成。 但它躲过了我。

原始问题:

我正在使用NSXMLParser从networkingstream中读取XML。 如果使用initWithContentsOfURL ,而接口可能会导致人们推断它将从Webstream式传输XML,但似乎并不这样做,而似乎是在任何分析发生之前尝试加载整个XML文件。 对于大小适中的XML文件来说很好,但对于真正大的XML文件来说,这是有问题的。

我已经看到使用NSXMLParserinitWithStream一起使用一些自定义的NSInputStream从networkingstream的讨论。 例如,有一些答案build议使用类似于下面的Cocoa Builder中提到的CFStreamCreateBoundPair和Apple Stream编程指南中的设置套接字stream的讨论,但是我还没有得到它的工作。 我甚至尝试编写自己的使用NSURLConnection的子类NSInputStream (这本身就非常适合stream式传输),但是我无法将它与NSXMLParser结合使用。

最后,我决定使用LibXML2而不是NSXMLParser ,正如Apple XMLPerformance示例中所演示的那样 ,但是我想知道是否有人从使用NSXMLParser的Web源获取stream媒体。 我已经看到了很多“理论上你可以做x ”的答案,从CFStreamCreateBoundPair到从NSURLRequest抓取HTTPBodyStream所有内容,但是我还没有碰到用NSXMLParser进行stream式处理的演示。

Ray Wenderlich的文章如何select最适合您的iPhone项目的XMLparsing器似乎证实NSXMLParser并不适合大型的XML文件,但是所有关于可能的基于NSXMLParser的解决scheme的文章都是针对真正大型的XML文件进行stream式处理的,我很惊讶,我还没有find这个工作示范。 有谁知道一个正常运行的NSXMLParser实现从networkingstream? 显然,我可以坚持使用LibXML2或其他一些等效的XMLparsing器,但是使用NSXMLParser进行stream式处理的概念看起来似乎非常接近。

-[NSXMLParser initWithStream:]-[NSXMLParser initWithStream:]的唯一接口,它现在执行数据的stream式parsing。 把它连接到一个递增提供数据的asynchronousNSURLConnection是不现实的,因为NSXMLParser采取了一种阻塞的,“拉”的方法来读取NSInputStream 。 也就是说,当处理一个NSInputStream时, -[NSXMLParser parse]做了如下的事情:

 while (1) { NSInteger length = [stream read:buffer maxLength:maxLength]; if (!length) break; // Parse data … } 

为了向这个parsing器递增地提供数据,需要一个自定义的NSInputStream子类,用于将NSURLConnectionDelegate调用接收到的数据通过背景队列或runloop传递给NSXMLParser正在等待的-read:maxLength: call。

概念certificate实现如下:

 #include <Foundation/Foundation.h> @interface ReceivedDataStream : NSInputStream <NSURLConnectionDelegate> @property (retain) NSURLConnection *connection; @property (retain) NSMutableArray *bufferedData; @property (assign, getter=isFinished) BOOL finished; @property (retain) dispatch_semaphore_t semaphore; @end @implementation ReceivedDataStream - (id)initWithContentsOfURL:(NSURL *)url { if (!(self = [super init])) return nil; NSURLRequest *request = [NSURLRequest requestWithURL:url]; self.connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO] autorelease]; self.connection.delegateQueue = [[[NSOperationQueue alloc] init] autorelease]; self.bufferedData = [NSMutableArray array]; self.semaphore = dispatch_semaphore_create(0); return self; } - (void)dealloc { self.connection = nil; self.bufferedData = nil; self.semaphore = nil; [super dealloc]; } - (BOOL)hasBufferedData { @synchronized (self) { return self.bufferedData.count > 0; } } #pragma mark - NSInputStream overrides - (void)open { NSLog(@"open"); [self.connection start]; } - (void)close { NSLog(@"close"); [self.connection cancel]; } - (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)maxLength { NSLog(@"read:%p maxLength:%ld", buffer, maxLength); if (self.isFinished && !self.hasBufferedData) return 0; if (!self.hasBufferedData) dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); NSAssert(self.isFinished || self.hasBufferedData, @"Was woken without new information"); if (self.isFinished && !self.hasBufferedData) return 0; NSData *data = nil; @synchronized (self) { data = [[self.bufferedData[0] retain] autorelease]; [self.bufferedData removeObjectAtIndex:0]; if (data.length > maxLength) { NSData *remainingData = [NSData dataWithBytes:data.bytes + maxLength length:data.length - maxLength]; [self.bufferedData insertObject:remainingData atIndex:0]; } } NSUInteger copiedLength = MIN([data length], maxLength); memcpy(buffer, [data bytes], copiedLength); return copiedLength; } #pragma mark - NSURLConnetionDelegate methods - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { NSLog(@"connection:%@ didReceiveData:…", connection); @synchronized (self) { [self.bufferedData addObject:data]; } dispatch_semaphore_signal(self.semaphore); } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSLog(@"connectionDidFinishLoading:%@", connection); self.finished = YES; dispatch_semaphore_signal(self.semaphore); } @end @interface ParserDelegate : NSObject <NSXMLParserDelegate> @end @implementation ParserDelegate - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict { NSLog(@"parser:%@ didStartElement:%@ namespaceURI:%@ qualifiedName:%@ attributes:%@", parser, elementName, namespaceURI, qualifiedName, attributeDict); } - (void)parserDidEndDocument:(NSXMLParser *)parser { NSLog(@"parserDidEndDocument:%@", parser); CFRunLoopStop(CFRunLoopGetCurrent()); } @end int main(int argc, char **argv) { @autoreleasepool { NSURL *url = [NSURL URLWithString:@"http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml"]; ReceivedDataStream *stream = [[ReceivedDataStream alloc] initWithContentsOfURL:url]; NSXMLParser *parser = [[NSXMLParser alloc] initWithStream:stream]; parser.delegate = [[[ParserDelegate alloc] init] autorelease]; [parser performSelector:@selector(parse) withObject:nil afterDelay:0.0]; CFRunLoopRun(); } return 0; }