iOS8 NSXMLParser crash

心不动则不痛 提交于 2020-01-10 10:44:08

问题


I had a crash in NSXMLParser

* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'NSXMLParser does not support reentrant parsing.'

Here is my code

NSString *wrappedSnippet = [NSString stringWithFormat:@"<html>%@</html>", self.snippet];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:[wrappedSnippet dataUsingEncoding:NSUTF8StringEncoding]];
[parser setDelegate:self];
[parser parse];

app crashes on the last line.

Note, that everything works perfect on iOS7!


回答1:


iOS8 throws an exception that previous versions caught and handled in the background.
see manual As from ios 5 NSXMLParser is thread safe but not reentrant! make sure you aren't calling parse from your NSXMLParser delegate. "Self" in your case.




回答2:


dispatch_queue_t reentrantAvoidanceQueue = dispatch_queue_create("reentrantAvoidanceQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(reentrantAvoidanceQueue, ^{
        NSXMLParser* parser = [[NSXMLParser alloc] initWithData:xml];
        [parser setDelegate:self];
        if (![parser parse]) {
            NSLog(@"There was an error=%@ parsing the xml. with data %@", [parser parserError], [[NSString alloc] initWithData:xml encoding: NSASCIIStringEncoding]);
        }
        [parser release];
    });
    dispatch_sync(reentrantAvoidanceQueue, ^{ });

Replace your code with above lines, Hope it helps you!




回答3:


I resolved my problem by dispatching parser in background queue!

NSXMLParser is now threadsafe. However, it is not reentrant on a given thread; don't call -parse on an NSXMLParser from within a delegate callback of another NSXMLParser.

- (void)parseWithCompletion:(ParserHandler)handler {
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
     dispatch_async(queue, ^{
          self.handler = handler;
          [self parse];
     });
}

- (void)parserDidEndDocument:(NSXMLParser *)parser {
     dispatch_async(dispatch_get_main_queue(), ^{
          if (self.handler) {
             self.handler(YES, self.dictionary, nil);
             self.handler = nil;
          }
     });
}



回答4:


In this issue, that means you could re-call [NSXMLParser parse] function in anything of his delegate. Sometimes you may call [parser parse] in parserDidEndDocument:
But it will notify you that this's a reentrant error!

So, the solution is, either you could [parser parser] in different queue,
for example, you could do it via calling dispatch_async(dispatch_get_main_queue(), ^{ do in block});

or, you need to adjust your call flow,
make sure that you won't call parse function in delegate.




回答5:


I had the same problem and wrote a subclass based upon NSXMLParser which handles the case:

      class SynchronizedXMLParser: NSXMLParser
    {
        // shared queue
        private static let _serialQueue: NSOperationQueue = {
            let queue = NSOperationQueue()
            queue.qualityOfService = NSQualityOfService.UserInitiated
            // making it serial on purpose in order to avoid 
            // the "reentrant parsing" issue
            queue.maxConcurrentOperationCount = 1
            return queue
        }()

        // instance level convenience accessor
        private var _serialQueue: NSOperationQueue
        {
            get
            {
                return self.dynamicType._serialQueue
            }
        }

        private weak var _associatedParsingTask: NSBlockOperation?

        deinit
        {
            _associatedParsingTask?.cancel()
        }

    //MARK: - Overridden

        required override init(data: NSData)
        {
            super.init(data: data)
        }

        // still unsafe to call within the delegate callbacks
        override func parse() -> Bool
        {
            var parsingResult = false

            if (_associatedParsingTask == nil)
            {
            let parsingTask = NSBlockOperation(block: {() -> Void in
                parsingResult = super.parse()
            })
            _associatedParsingTask = parsingTask
            // making it synchronous in order to return the result 
            // of the super's parse call
            _serialQueue.addOperations([parsingTask], waitUntilFinished: true)
        }

        return parsingResult
    }

    override func abortParsing()
    {
        if let parsingTask = _associatedParsingTask
        {
            parsingTask.cancel()
            _associatedParsingTask = nil
        }

        super.abortParsing()
    }

    // MARK: - Introduced 

        // safe to use everywhere as it doesn't force the calling thread to wait until this me

thod returns
    func parse(completion completion:(Bool) -> Void) -> Void
    {
        var parsingResult = false

        if (_associatedParsingTask == nil)
        {
            let parsingTask = NSBlockOperation(block: {() -> Void in
                parsingResult = super.parse()
            })
            parsingTask.completionBlock = { () -> Void in
                completion(parsingResult)
            }
            _associatedParsingTask = parsingTask
            // making it synchronous in order to return the result
            // of the super's parse call
            _serialQueue.addOperation(parsingTask)
        }


       }
    }

P.S. The idea is pretty much the same as what @CrimeZone suggested.



来源:https://stackoverflow.com/questions/25363560/ios8-nsxmlparser-crash

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!