# NSRange

By [Mattt](https://nshipster.com/authors/mattt).
Published 2014-01-13.
Canonical URL: <https://nshipster.com/nsrange>

`NSRange` is one of the essential types of Foundation. Passed around and returned in methods throughout the framework, being well-versed in this struct has a range of benefits, which this week’s article will help you locate.

---

Ranges are data types used to describe a contiguous interval of integers. They are most commonly used with strings, arrays, and similarly-ordered collections.

For Objective-C programs, the Foundation type `NSRange` is used. In other languages, ranges are often encoded as a two-element array, containing the start and end indexes. In Foundation, `NSRange` instead encodes a range as a struct containing the location and length. By command-clicking (`⌘-ʘ`) on the `NSRange` symbol in Xcode, we can jump directly to its declaration in `Foundation/NSRange.h`:

```objc
typedef struct _NSRange {
    NSUInteger location;
    NSUInteger length;
} NSRange;
```

In practice, this approach helps mitigate common off-by-one errors when working with ranges. For example, compare the equivalent Javascript and Objective-C code for creating a range of characters for a given string:

#### range.js

```javascript
var string = "hello, world";
var range = [0, string.length - 1];
```

Forgetting to subtract `1` for the end index in Javascript would result in an out-of-bounds error later.

#### range.m

```objc
NSString *string = @"hello, world";
NSRange range = NSMakeRange(0, [string length]);
```

`NSRange`’s approach is clearer and less prone to error—especially when it comes to more complex arithmetic operations on ranges.

## Usage

### Strings

```objc
NSString *string = @"lorem ipsum dolor sit amet";
NSRange range = [string rangeOfString:@"ipsum"];
// {.location=6, .length=5}

NSString *substring = [string substringWithRange:range];
// @"ipsum"
```

`NSString` does not have a method like `containsString:`. Instead, `rangeOfString:` can be used to check for an `NSNotFound` location value:

```objc
NSString *input = ...;
if ([input rangeOfString:@"keyword"].location != NSNotFound) {
    <#…#>
}
```

### Arrays

```objc
NSArray *array = @[@"a", @"b", @"c", @"d"];
NSArray *subarray = [array subarrayWithRange:NSMakeRange(1, 2)];
// @[@"b", @"c"]
```

### Index Sets

[NSIndexSet](https://nshipster.com/nsindexset/) is a Foundation collection class that is similar to `NSRange`, with the notable exception of being able to support non-contiguous series. An `NSIndexSet` can be created from a range using the `indexSetWithIndexesInRange:` class constructor:

```objc
NSRange range = NSMakeRange(0, 10);
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
```

## Functions

Because `NSRange` is not a class, creating and using instances is done through function calls, rather than, say, instance methods.

> Many of the NSRange functions are named counter to the modern conventions of Foundation and CoreFoundation wherein the relevant type of the function immediately follows the two-letter namespace. For example, `NSMakeRange` should instead be named `NSRangeMake`, following the example of `CGRectMake` and `CGSizeMake`, et al. Similarly, a better name for `NSEqualRanges` would be `NSRangeEqualToRange`, just like `CGPointEqualToPoint`.
>
> Although consistency in itself is likely not sufficient reason to go through the trouble of replacing existing usage, [this gist](https://gist.github.com/mattt/8402537) shows how one could make their own code base a little more OCD-friendly.

### Creating an NSRange

> - `NSMakeRange`: Creates a new NSRange from the specified values.

```objc
NSArray *array = @[@1, @2, @3];
NSRange range = NSMakeRange(0, [array count]);
// {.location=0, .length=3}
```

### Querying Information

> - `NSEqualRanges`: Returns a Boolean value that indicates whether two given ranges are equal.

```objc
NSRange range1 = NSMakeRange(0, 6);
NSRange range2 = NSMakeRange(2, 7);
BOOL equal = NSEqualRanges(range1, range2); // NO
```

> - `NSLocationInRange`: Returns a Boolean value that indicates whether a specified position is in a given range.

```objc
NSRange range = NSMakeRange(3, 4);
BOOL contained = NSLocationInRange(5, range); // YES
```

> - `NSMaxRange`: Returns the sum of the location and length of the range.

```objc
NSRange range = NSMakeRange(3, 4);
NSUInteger max = NSMaxRange(range); // 7
```

### Set Operations

> - `NSIntersectionRange`: Returns the intersection of the specified ranges. If the returned range’s length field is `0`, then the two ranges don’t intersect, and the value of the location field is undefined.

```objc
NSRange range1 = NSMakeRange(0, 6);
NSRange range2 = NSMakeRange(2, 7);
NSRange intersectionRange = NSIntersectionRange(range1, range2);
// {.location=2, .length=4}
```

> - `NSUnionRange`: Returns the union of the specified ranges. A range covering all indices in and between range1 and range2. If one range is completely contained in the other, the returned range is equal to the larger range.

```objc
NSRange range1 = NSMakeRange(0, 6);
NSRange range2 = NSMakeRange(2, 7);
NSRange unionRange = NSUnionRange(range1, range2);
// {.location=0, .length=9}
```

### Converting Between NSString \* & NSRange

> - `NSStringFromRange`: Returns a string representation of a range.

```objc
NSRange range = NSMakeRange(3, 4);
NSString *string = NSStringFromRange(range); // @"{3,4}"
```

> - `NSRangeFromString`: Returns a range from a textual representation.

```objc
NSString *string = @"{1,5}";
NSRange range = NSRangeFromString(string);
// {.location=1, .length=5}
```

If the string passed into `NSRangeFromString` does not represent a valid range, it will return a range with its location and length set to `0`.

```objc
NSString *string = @"invalid";
NSRange range = NSRangeFromString(string);
// {.location=0, .length=0}
```

While one might be tempted to use `NSStringFromRange` to box `NSRange` for inclusion within an `NSArray`, `NSValue +valueWithRange:` is the way to go:

```objc
NSRange range = NSMakeRange(0, 3);
NSValue *value = [NSValue valueWithRange:range];
```

---

`NSRange` is one of the few cases where some of the underlying implementation of its functions are actually exposed and inlined in the public headers:

#### Foundation/NSRange.h

```objc
NS_INLINE NSRange NSMakeRange(NSUInteger loc, NSUInteger len) {
    NSRange r;
    r.location = loc;
    r.length = len;
    return r;
}

NS_INLINE NSUInteger NSMaxRange(NSRange range) {
    return (range.location + range.length);
}

NS_INLINE BOOL NSLocationInRange(NSUInteger loc, NSRange range) {
    return (!(loc < range.location) && (loc - range.location) < range.length) ? YES : NO;
}

NS_INLINE BOOL NSEqualRanges(NSRange range1, NSRange range2) {
    return (range1.location == range2.location && range1.length == range2.length);
}
```

## NSRangePointer

One oddity worth mentioning with `NSRange` is the existence of `NSRangePointer`. “What the what?”, you might exclaim in panicked confusion. Jumping to the source confirms our darkest fears:

#### Foundation/NSRange.h

```objc
typedef NSRange *NSRangePointer;
```

So. Without a definitive origin story, one would have to assume that this type was created by a well-meaning framework engineer who noted the confusion around `NSRange` being a struct and not a class. `NSRange *` is equivalent to `NSRangePointer`, though the latter can be found in out parameters for various methods throughout Foundation. `NSAttributedString`, for instance, has an `NSRangePointer` parameter for returning the effective range of an attribute at a particular index (since the attribute likely starts and ends outside of the specified index):

```objc
NSMutableAttributedString *mutableAttributedString = ...;
NSRange range;
if ([mutableAttributedString attribute:NSUnderlineStyleAttributeName
                               atIndex:0
                        effectiveRange:&range])
{
    // Make underlined text blue as well
    [mutableAttributedString addAttribute:NSForegroundColorAttributeName
                                    value:[UIColor blueColor]
                                    range:range];
}
```

## CFRange

One final caveat: Core Foundation also defines a `CFRange` type, which differs from `NSRange` in using `CFIndex` types for its members, and having only the function `CFRangeMake`:

```objc
typedef struct {
    CFIndex location;
    CFIndex length;
} CFRange;
```

Anyone working with CoreText or another low-level C API is likely to encounter `CFRange` in place of `NSRange`.
