textViewDidChange: crashes in iOS 7

2019-03-30 01:54发布

As we know the UITextView is more powerful in iOS 7, but here it performs more fragile. The following code works fine when inputting "rr" with Chinese input keyboard in iOS 6, but crashes in iOS 7.

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.tv = [[UITextView alloc] initWithFrame:CGRectMake(0, 100, 320, 100)];
    self.tv.backgroundColor = [UIColor yellowColor];
    self.tv.textColor = [UIColor blackColor];
    self.tv.editable = YES;
    self.tv.delegate = self;

    [self.view addSubview:self.tv];
}

-(void)textViewDidChange:(UITextView *)textView{

    int maxLength = 2;
    if (self.tv.text.length > maxLength) {
        NSLog(@"self.tv.text :%@",self.tv.text);
        NSLog(@"self.tv.text.length :%d",self.tv.text.length);
        NSLog(@"maxLength :%d",maxLength);
        self.tv.text = [self.tv.text substringToIndex:maxLength];
    }
}

The log is as following:

2013-11-13 15:48:16.003 Test2[1388:70b] self.tv.text :r r
2013-11-13 15:48:16.004 Test2[1388:70b] self.tv.text.length :3
2013-11-13 15:48:16.005 Test2[1388:70b] maxLength :2
2013-11-13 15:48:16.032 Test2[1388:70b] *** Terminating app due to uncaught exception 'NSRangeException', reason: 'NSMutableRLEArray replaceObjectsInRange:withObject:length:: Out of bounds'
*** First throw call stack:
(
    0   CoreFoundation                      0x017355e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x014b88b6 objc_exception_throw + 44
    2   CoreFoundation                      0x017353bb +[NSException raise:format:] + 139
    3   Foundation                          0x010ebba1 -[NSMutableRLEArray replaceObjectsInRange:withObject:length:] + 136
    4   Foundation                          0x010ebb14 -[NSMutableRLEArray deleteObjectsInRange:] + 63
    5   Foundation                          0x010ea559 -[NSConcreteMutableAttributedString replaceCharactersInRange:withAttributedString:] + 324
    6   UIFoundation                        0x02d9d9f4 __71-[NSConcreteTextStorage replaceCharactersInRange:withAttributedString:]_block_invoke + 68
    7   UIFoundation                        0x02d9d92f -[NSConcreteTextStorage replaceCharactersInRange:withAttributedString:] + 121
    8   UIKit                               0x00924d22 __53-[UITextInputController setMarkedText:selectedRange:]_block_invoke + 352
    9   UIFoundation                        0x02d9b491 -[NSTextStorage coordinateEditing:] + 48
    10  UIKit                               0x00924a38 -[UITextInputController setMarkedText:selectedRange:] + 249
    11  UIKit                               0x008fb3b1 -[UITextView setMarkedText:selectedRange:] + 63
    12  UIKit                               0x00644aa5 -[UIResponder(UITextInput_Internal) _setMarkedText:selectedRange:] + 101
    13  UIKit                               0x004031aa -[UIKeyboardImpl setMarkedText:selectedRange:inputString:searchString:] + 365
    14  UIKit                               0x00419737 -[TIKeyboardOperationSetMarkedText(UIKeyboardImpl) main] + 178
    15  Foundation                          0x0118da69 -[__NSOperationInternal _start:] + 671
    16  Foundation                          0x0110a798 -[NSOperation start] + 83
    17  UIKit                               0x0040e08a -[UIKeyboardImpl updateCandidateDisplayAsyncWithCandidateSet:documentOperation:] + 188
    18  UIKit                               0x00419041 -[TIKeyboardOperationSetCandidates(UIKeyboardImpl) main] + 112
    19  Foundation                          0x0118da69 -[__NSOperationInternal _start:] + 671
    20  Foundation                          0x0110a798 -[NSOperation start] + 83
    21  UIKit                               0x0040671d -[UIKeyboardImpl performOperations:] + 153
    22  UIKit                               0x00404f4e -[UIKeyboardImpl continueGenerateCandidatesAsynchronouslyWithOperations:] + 40
    23  UIKit                               0x00404c51 __87-[UIKeyboardImpl replyHandlerForGenerateCandidatesAsynchronouslyWithSelectedCandidate:]_block_invoke_2 + 46
    24  UIKit                               0x009381b8 -[UIKeyboardTaskQueue continueExecutionOnMainThread] + 402
    25  UIKit                               0x0093885f -[UIKeyboardTaskQueue addTask:] + 144
    26  CoreFoundation                      0x01729d1d __invoking___ + 29
    27  CoreFoundation                      0x01729c2a -[NSInvocation invoke] + 362
    28  UIKit                               0x008e63d2 -[_UIActionWhenIdle invoke] + 100
    29  UIKit                               0x008e64a6 __41-[_UIActionWhenIdle addObserverToRunLoop]_block_invoke + 36
    30  CoreFoundation                      0x0172924d _runLoopObserverWithBlockContext + 29
    31  CoreFoundation                      0x016fd53e __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30
    32  CoreFoundation                      0x016fd48f __CFRunLoopDoObservers + 399
    33  CoreFoundation                      0x016db3b4 __CFRunLoopRun + 1076
    34  CoreFoundation                      0x016dab33 CFRunLoopRunSpecific + 467
    35  CoreFoundation                      0x016da94b CFRunLoopRunInMode + 123
    36  GraphicsServices                    0x036d69d7 GSEventRunModal + 192
    37  GraphicsServices                    0x036d67fe GSEventRun + 104
    38  UIKit                               0x0022b94b UIApplicationMain + 1225
    39  Test2                               0x0000388d main + 141
    40  libdyld.dylib                       0x01d7370d start + 1
    41  ???                                 0x00000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

In addition, I test the following code, it also crashes after input "rr" with Chinese input keyboard.

-(void)textViewDidChange:(UITextView *)textView{

    int maxLength = 2;
    if (self.tv.text.length > maxLength) {
        self.tv.text = @"c";
    }
}

Any help will be appreciated!

1条回答
一纸荒年 Trace。
2楼-- · 2019-03-30 02:27

What's happening is that you're typing what is referred to as multistage text input, i.e. the input has to be confirmed from the user before it's actually committed into the underlying text. You get the crash because the text is only kind-of inputted, and until the text has been committed, it could easily change to something else.

To detect this situation, you're looking for the markedTextRange property of the UITextView, which indicates if you're in this complex input mode.

If the property is non-nil, then you're in this special input mode, so you should guard your modification code with something like:

if (self.tv.markedTextRange == nil && self.tv.text.length > maxLength) {
    // Perform change
}

Near as I can tell the crash is triggered by the special multistage text input mode, so you should avoid changing the text while this mode is active.

查看更多
登录 后发表回答