iOS8 NSXMLParser崩溃

时间:2014-08-18 12:40:52

标签: nsxmlparser ios8

我在NSXMLParser中遇到了崩溃

  

* 由于未捕获的异常'NSInternalInconsistencyException'而终止应用程序,原因:'NSXMLParser不支持可重入解析。'

这是我的代码

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

应用程序在最后一行崩溃。

请注意,iOS7上的一切都很完美!

5 个答案:

答案 0 :(得分:9)

iOS8抛出一个异常,即先前版本在后台捕获并处理。
see manual从ios 5开始,NSXMLParser是线程安全的,但不是可重入的!确保您没有从NSXMLParser委托调用解析。在你的情况下“自我”。

答案 1 :(得分:7)

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, ^{ });

用上面的代码替换你的代码,希望它对你有帮助!

答案 2 :(得分:6)

我通过在后台队列中调度解析器解决了我的问题!

  

NSXMLParser现在是线程安全的。但是,它在给定的线程上不可重入;不要在另一个NSXMLParser的委托回调中调用NSXMLParser上的-parse。

- (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;
          }
     });
}

答案 3 :(得分:0)

在本期中,这意味着您可以在其委托的任何内容中重新调用[NSXMLParser parse]函数。 有时您可以在parserDidEndDocument中调用[parser parse]:
但它会通知您这是一个可重入的错误!

所以,解决方法是,你可以[解析器解析器]在不同的队列中,
例如,您可以通过调用dispatch_async(dispatch_get_main_queue(),^ {do in block})来完成;

或者,您需要调整通话流量,
确保你不会在委托中调用解析函数。

答案 4 :(得分:0)

我遇到了同样的问题并编写了一个基于NSXMLParser的子类来处理这个案例:

      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。这个想法与@CrimeZone建议的几乎相同。