我正在尝试使用 NSLinguisticTagger 来监视 NSTextStorage 的内容并根据用户键入的内容提供一些上下文信息。为此,我有一个 OverlayManager 对象,它连接了这种关系:
-(void) setView:(NSTextView*) view {
_view = view;
_layout = view.layoutManager;
_storage = view.layoutManager.textStorage; //get the TextStorage from the view
[_tagger setString:_storage.string]; //pull the string out, this grabs the mutable version
[self registerForNotificationsOn:self->_storage]; //subscribe to the willProcessEditing notification
}
当编辑发生时,我确保捕获它并通知标记器(是的,我知道我与成员访问不一致,我对 Obj-C 生疏了,我稍后会修复它):
- (void) textStorageWillProcessEditing:(NSNotification*) notification{
if ([self->_storage editedMask] & NSTextStorageEditedCharacters) {
NSRange editedRange = [self->_storage editedRange];
NSUInteger delta = [self->_storage changeInLength];
[_tagger stringEditedInRange:editedRange changeInLength:delta]; //should notify the tagger of the changes- if I replace this line with [_tagger setString:[_storage string]] it works, but gobbles CPU as it retags the entire string
[self highlightEdits:self];
}
}
highlightEdits 消息将作业委托给“覆盖”对象池。每个都包含一个与此类似的代码块:
[tagger enumerateTagsInRange:range scheme:NSLinguisticTagSchemeLexicalClass options:0 usingBlock:^(NSString *tag, NSRange tokenRange, NSRange sentenceRange, BOOL *stop) {
if (tag == PartOfSpeech) {
[self applyHighlightToRange:tokenRange onStorage:storage];
}
}];
这就是问题所在 - enumerateTagsInRange 方法崩溃并显示一条消息:
2014-06-04 10:07:19.692 WritersEditor[40191:303] NSMutableRLEArray replaceObjectsInRange:withObject:length:: Out of bounds
如果我不链接到底层字符串的可变副本而是执行 a ,则不会发生此问题[[_storage string] copy]
,但显然我不想在每次要进行标记时都复制整个后备存储。这一切都应该发生在主运行循环中,所以我认为这不是线程问题。我枚举标签的 NSRange 存在于 NSTextStorage和NSLinguisticTagger 的字符串视图中。applyHighlightToRange 调用甚至不会向字符串添加属性,因为它甚至在到达该行之前就崩溃了。
我试图围绕该问题构建一个测试用例,但在这些情况下无法复制它:
- (void) testEdit
{
NSAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Quickly, this is a test."];
text = [[NSTextStorage alloc] initWithAttributedString:str];
NSArray* schemes = [NSLinguisticTagger availableTagSchemesForLanguage:@"en"];
tagger = [[NSLinguisticTagger alloc] initWithTagSchemes:schemes options:0];
[tagger setString:[text string]];
[text beginEditing];
[[text mutableString] appendString:@"T"];
NSRange edited = [text editedRange];
NSUInteger length = [text changeInLength];
[text endEditing];
[tagger stringEditedInRange:edited changeInLength:length];
[tagger enumerateTagsInRange:edited scheme:NSLinguisticTagSchemeLexicalClass options:0 usingBlock:^(NSString *tag, NSRange tokenRange, NSRange sentenceRange, BOOL *stop) {
//doesn't matter, this should crash
}];
}
该代码不会崩溃。