Skip to content

Commit

Permalink
Reverting to before Swift addition.
Browse files Browse the repository at this point in the history
  • Loading branch information
natecook1000 committed Oct 1, 2015
1 parent 5edb957 commit 72ca69d
Showing 1 changed file with 17 additions and 130 deletions.
147 changes: 17 additions & 130 deletions 2014-01-13-nsrange.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ author: Mattt Thompson
category: Cocoa
excerpt: "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."
status:
swift: 2.0
reviewed: September 15, 2015
swift: t.b.c.
---

`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.
Expand All @@ -16,15 +15,6 @@ Ranges are data types used to describe a contiguous interval of integers. They a

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 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`:

~~~{swift}
public struct _NSRange {
public var location: Int
public var length: Int
public init()
public init(location: Int, length: Int)
}
~~~

~~~{objective-c}
typedef struct _NSRange {
NSUInteger location;
Expand All @@ -45,11 +35,6 @@ Forgetting to subtract `1` for the end index in Javascript would result in an ou

#### range.m

~~~{swift}
let string = "hello, world"
let range = NSMakeRange(0, string.characters.count)
~~~

~~~{objective-c}
NSString *string = @"hello, world";
NSRange range = NSMakeRange(0, [string length]);
Expand All @@ -61,29 +46,17 @@ NSRange range = NSMakeRange(0, [string length]);

### Strings

~~~{swift}
let string: NSString = "lorem ipsum dolor sit amet"
let range = string.rangeOfString("ipsum") // {.location=6, .length=5}
let substring = string.substringWithRange(range) // "ipsum"
~~~

~~~{objective-c}
NSString *string = @"lorem ipsum dolor sit amet";
NSRange range = [string rangeOfString:@"ipsum"]; // {.location=6, .length=5}
NSRange range = [string rangeOfString:@"ipsum"];
// {.location=6, .length=5}
NSString *substring = [string substringWithRange:range]; // @"ipsum"
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:

~~~{swift}
let input: NSString = ...
if input.rangeOfString("keyword").location != NSNotFound {
// ...
}
~~~

~~~{objective-c}
NSString *input = ...;
if ([input rangeOfString:@"keyword"].location != NSNotFound) {
Expand All @@ -93,25 +66,16 @@ if ([input rangeOfString:@"keyword"].location != NSNotFound) {

### Arrays

~~~{swift}
let array: NSArray = ["a", "b", "c", "d"]
let subArray = array.subarrayWithRange(NSMakeRange(1, 2)) // ["b", "c"]
~~~

~~~{objective-c}
NSArray *array = @[@"a", @"b", @"c", @"d"];
NSArray *subArray = [array subarrayWithRange:NSMakeRange(1, 2)]; // @[@"b", @"c"]
NSArray *subarray = [array subarrayWithRange:NSMakeRange(1, 2)];
// @[@"b", @"c"]
~~~

### Index Sets

[NSIndexSet](http://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:

~~~{swift}
let range = NSMakeRange(0, 10)
let indexSet = NSIndexSet(indexesInRange: range)
~~~

~~~{objective-c}
NSRange range = NSMakeRange(0, 10);
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
Expand All @@ -129,26 +93,16 @@ Because `NSRange` is not a class, creating and using instances is done through f

> - `NSMakeRange`: Creates a new NSRange from the specified values.
~~~{swift}
let array: NSArray = [1, 2, 3]
let range = NSMakeRange(0, array.count) // {.location=0, .length=3}
~~~

~~~{objective-c}
NSArray *array = @[@1, @2, @3];
NSRange range = NSMakeRange(0, [array count]); // {.location=0, .length=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.
~~~{swift}
let range1 = NSMakeRange(0, 6)
let range2 = NSMakeRange(2, 7)
let equal = NSEqualRanges(range1, range2) // false
~~~

~~~{objective-c}
NSRange range1 = NSMakeRange(0, 6);
NSRange range2 = NSMakeRange(2, 7);
Expand All @@ -157,23 +111,13 @@ BOOL equal = NSEqualRanges(range1, range2); // NO

> - `NSLocationInRange`: Returns a Boolean value that indicates whether a specified position is in a given range.
~~~{swift}
let range = NSMakeRange(3, 4)
let contained = NSLocationInRange(5, range) // true
~~~

~~~{objective-c}
NSRange range = NSMakeRange(3, 4);
BOOL contained = NSLocationInRange(5, range); // YES
~~~

> - `NSMaxRange`: Returns the sum of the location and length of the range.
~~~{swift}
let range = NSMakeRange(3, 4)
let max = NSMaxRange(range) // 7
~~~

~~~{objective-c}
NSRange range = NSMakeRange(3, 4);
NSUInteger max = NSMaxRange(range); // 7
Expand All @@ -183,77 +127,49 @@ NSUInteger max = NSMaxRange(range); // 7

> - `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.
~~~{swift}
let range1 = NSMakeRange(0, 6)
let range2 = NSMakeRange(2, 7)
let intersectionRange = NSIntersectionRange(range1, range2) // {.location=2, .length=4}
~~~

~~~{objective-c}
NSRange range1 = NSMakeRange(0, 6);
NSRange range2 = NSMakeRange(2, 7);
NSRange intersectionRange = NSIntersectionRange(range1, range2); // {.location=2, .length=4}
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.
~~~{swift}
let range1 = NSMakeRange(0, 6)
let range2 = NSMakeRange(2, 7)
let unionRange = NSUnionRange(range1, range2) // // {.location=0, .length=9}
~~~

~~~{objective-c}
NSRange range1 = NSMakeRange(0, 6);
NSRange range2 = NSMakeRange(2, 7);
NSRange unionRange = NSUnionRange(range1, range2); // {.location=0, .length=9}
NSRange unionRange = NSUnionRange(range1, range2);
// {.location=0, .length=9}
~~~

### Converting Between NSString * & NSRange

> - `NSStringFromRange`: Returns a string representation of a range.
~~~{swift}
let range = NSMakeRange(3, 4)
let string = NSStringFromRange(range) // "{3,4}"
~~~

~~~{objective-c}
NSRange range = NSMakeRange(3, 4);
NSString *string = NSStringFromRange(range); // @"{3,4}"
~~~

> - `NSRangeFromString`: Returns a range from a textual representation.
~~~{swift}
let string = "{1,5}"
let range = NSRangeFromString(string) // {.location=1, .length=5}
~~~

~~~{objective-c}
NSString *string = @"{1,5}";
NSRange range = NSRangeFromString(string); // {.location=1, .length=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`.

~~~{swift}
let string = "invalid"
let range = NSRangeFromString(string) // {.location=0, .length=0}
~~~

~~~{objective-c}
NSString *string = @"invalid";
NSRange range = NSRangeFromString(string); // {.location=0, .length=0}
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:

~~~{swift}
let range = NSMakeRange(0, 3)
let value = NSValue(range: range)
~~~

~~~{objective-c}
NSRange range = NSMakeRange(0, 3);
NSValue *value = [NSValue valueWithRange:range];
Expand Down Expand Up @@ -292,35 +208,15 @@ One oddity worth mentioning with `NSRange` is the existence of `NSRangePointer`.

#### Foundation/NSRange.h

~~~{swift}
public typealias NSRangePointer = UnsafeMutablePointer<NSRange>
~~~

~~~{objective-c}
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 before outside of the specified index):

~~~{swift}
let mutableAttributedString: NSMutableAttributedString = ...
var range = NSRange()
if let _ = mutableAttributedString.attribute(NSUnderlineStyleAttributeName,
atIndex: 0,
effectiveRange: &range)
{
mutableAttributedString.addAttribute(NSForegroundColorAttributeName,
value: UIColor.blueColor(),
range: range
)
}
~~~

~~~{objective-c}
NSMutableAttributedString *mutableAttributedString = ...;
NSRange range;
if ([mutableAttributedString attribute:NSUnderlineStyleAttributeName
atIndex:0
effectiveRange:&range])
Expand All @@ -336,15 +232,6 @@ if ([mutableAttributedString attribute:NSUnderlineStyleAttributeName

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`:

~~~{swift}
public struct CFRange {
public var location: CFIndex
public var length: CFIndex
public init()
public init(location: CFIndex, length: CFIndex)
}
~~~

~~~{objective-c}
typedef struct {
CFIndex location;
Expand Down

0 comments on commit 72ca69d

Please sign in to comment.