objective-cnsintegernsrangeexception

Why does this conditional cause an error?


I have a method that contains a few conditionals. The first conditional works fine and does not cause any problems. However, the second one causes the app to crash.

- (void)didReceiveGaiaGattResponse:(CSRGaiaGattCommand *)command
{
    GaiaCommandType cmdType = [command getCommandId];
    NSData *requestPayload = [command getPayload];
    uint8_t success = 0;

    NSLog(@"cmdType: %li", (long)cmdType);
    [requestPayload getBytes:&success range:NSMakeRange(0, sizeof(uint8_t))];

    if (cmdType == GaiaCommand_GetCurrentBatteryLevel && requestPayload.length > 1)
    {
        uint16_t value = 0;
        [requestPayload getBytes:&value range:NSMakeRange(1, sizeof(uint16_t))];
        NSInteger battery = CFSwapInt16BigToHost(value);

        [self sendEventWithName:someDEVICE_BATTERY_CHANGED body:@{@"batteryLevel":[NSNumber numberWithInteger:battery]}];
        return;
    }
     else if (cmdType == GaiaCommand_GET_FBC && requestPayload.length > 1)
    {
         uint16_t value = 0;
         [requestPayload getBytes:&value range:NSMakeRange(1, sizeof(uint16_t))];
         NSInteger feedbackCancellationMode = CFSwapInt16BigToHost(value);
         [self sendEventWithName:FEEDBACK_CANCELLATION_MODE body:@{@"feedbackCancellationMode": [NSNumber numberWithInt:feedbackCancellationMode]}];
         return;
    }

    //do more stuff
}

The conditional

if (cmdType == GaiaCommand_GetCurrentBatteryLevel && requestPayload.length > 1)

works without problems.

However, the conditional

else if (cmdType == GaiaCommand_GET_FBC && requestPayload.length > 1)

causes the following warning in xcode

Implicit conversion loses integer precision: 'NSInteger' (aka 'long') to 'int'

In addition, I also saw the error message in the debugger

* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[_NSInlineData getBytes:range:]: range {1, 2} exceeds

data length 2'


Solution

  • Consider what this is telling you:

    Terminating app due to uncaught exception 'NSRangeException', reason:
    '-[_NSInlineData getBytes:range:]: range {1, 2} exceeds data length 2'
    

    Your data object is 2 bytes in length. The first byte, at position 0, is (according to your code) the success value. That leaves one more byte at position 1 to handle. But your code is attempt to copy 2 bytes out of it — that's the range {1, 2} in the message; a range starting at position 1 and with a length of 2. You're reading past the end of the data.

    You have to check that the data has enough data to satisfy the -getBytes:... call you're attempting to make. You may also need to correct your assumptions about how large the cancellation mode value in the buffer is supposed to be, because it's apparently smaller than you expect. Your code assumes it's a uint16_t (2 bytes) but there's only one byte left in the data.