8

Say I pass the NSRange of (location: 5, length: 50) on the NSString "foo", that range obviously doesn't exist.

Is there a way to say [string rangeExists:NSRange] for instance, or do we have to manually validate the input?

Doug Smith
  • 29,668
  • 57
  • 204
  • 388
  • I suspect the only thing you can do is check the length of the string first, then use that to ensure within range and avoid the exception. – Gismay Dec 09 '15 at 22:46
  • There's two ways the range could be invalid: the `location` could be beyond the end by itself, or the `location` could be valid but the `length` too long. Does the distinction matter? – jscs Dec 09 '15 at 22:52

3 Answers3

26

You have to write your own check but it's simple enough:

NSString *str = ... // some string
NSRange range = ... // some range to be used on str

if (range.location != NSNotFound && range.location + range.length <= str.length) {
    // It's safe to use range on str
}

You could create a category method on NSString that adds your proposed rangeExists: method. It would just be:

- (BOOL)rangeExists:(NSRange)range {
    return range.location != NSNotFound && range.location + range.length <= self.length;
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • 1
    `range.location + range.length` could overflow/wrap around :) – Martin R Dec 09 '15 at 22:56
  • @MartinR Yeah but only in very rare cases. I did update to check `location` for `NSNotFound`. – rmaddy Dec 09 '15 at 22:58
  • 1
    Yes, I did not mean that too seriously. But `range.location <= str.length && range.length <= str.length - range.location` might cover all situations. – Martin R Dec 09 '15 at 23:02
  • 1
    @DougSmith Did this answer your question or are you looking for something different? – rmaddy Dec 14 '15 at 16:56
0

If range A contains range B, the intersection of A & B should be B, the union of A & B should be A. So in theory, both

NSEqualRanges(NSUnionRange(A, B), A)

and

NSIntersectionRange(A, B), B)

can be used to check if range A contains range B.

But if we pass a negative value to B.location, for the members of struct NSRange is NSUInteger, it will turn to a very large value. In this case,

NSUnionRange(A, B)

also returns A. It seems to be a bug. (Tested on macOS 10.14, Xcode 10.1)

so we choose NSIntersectionRange, like this:

NSString *str = @"foo";
NSRange textRange = NSMakeRange(0, str.length);
NSRange range = NSMakeRange(0, 1);
if (NSEqualRanges(NSIntersectionRange(textRange, range), range)) {
    //safe
}
  • 3
    An answer consisting only on code is not consider a proper SO answer. Please explain also your reasoning along with the code (from review) – FrankS101 Nov 07 '18 at 12:12
0

You can do this :

if(selectedNSRange.location != NSNotFound && NSMaxRange(selectedNSRange) <= self.text.length)
barryjones
  • 2,149
  • 1
  • 17
  • 21