NSLinguisticTagger on the contents of an NSTextStorage- crashing bug

Posted by Remy Porter on Stack Overflow See other posts from Stack Overflow or by Remy Porter
Published on 2014-06-04T14:18:51Z Indexed on 2014/06/04 15:25 UTC
Read the original article Hit count: 268

Filed under:
|
|

I'm trying to use an NSLinguisticTagger to monitor the contents of an NSTextStorage and provide some contextual information based on what the user types. To that end, I have an OverlayManager object, which wires up this relationship:

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

When an edit occurs, I make sure to trap it and notify the tagger (and yes, I know I'm being annoyingly inconsistent with member access, I'm rusty on Obj-C, I'll fix it later):

- (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
        [self highlightEdits:self];
    }
}

The highlightEdits message delegates the job out to a pool of "Overlay" objects. Each contains a block of code similar to this:

[tagger enumerateTagsInRange:range scheme:NSLinguisticTagSchemeLexicalClass options:0 usingBlock:^(NSString *tag, NSRange tokenRange, NSRange sentenceRange, BOOL *stop) {
            if (tag == PartOfSpeech) {
                [self applyHighlightToRange:tokenRange onStorage:storage];
            }
        }];

And that's where the problem is- the enumerateTagsInRange method crashes out with a message:

2014-06-04 10:07:19.692 WritersEditor[40191:303] NSMutableRLEArray     replaceObjectsInRange:withObject:length:: Out of bounds

This problem doesn't occur if I don't link to the mutable copy of the underlying string and instead do a [[_storage string] copy], but obviously I don't want to copy the entire backing store every time I want to do tagging. This all should be happening in the main run loop, so I don't think this is a threading issue. The NSRange I'm enumerating tags on exists both in the NSTextStorage and in the NSLinguisticTagger's view of the string. It's not even the fact that the applyHighlightToRange call adds attributes to the string, because it crashes before even reaching that line.

I attempted to build a test case around the problem, but can't replicate it in those situations:

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

That code doesn't crash.

© Stack Overflow or respective owner

Related posts about objective-c

Related posts about xcode