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 メッセージは、ジョブを「Overlay」オブジェクトのプールに委任します。それぞれに、次のようなコード ブロックが含まれています。
[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
この問題は、基になる文字列の変更可能なコピーにリンクせず、代わりに[[_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
}];
}
そのコードはクラッシュしません。