我在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上的一切都很完美!
答案 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建议的几乎相同。