What’s New in Objective-C and Foundation in iOS 7

iOS 7 brought some interesting new features to Objective-C and Foundation. New compiler features and improved Foundation classes – read all about them! By Matt Galloway.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

New return type – instancetype

A new type has been added to Objective-C, aptly named instancetype. This can only be used as a return type from an Objective-C method and is used as a hint to the compiler that the return type of the method will be an instance of the class to which the method belongs.

Note: This feature is not strictly new in Xcode 5 and iOS 7, but has been stealthily dropped into recent Clang builds over time. However, Xcode 5 marks the first time that Apple has started using it throughout their frameworks. You can read more about this language feature on the official Clang website: http://clang.llvm.org/docs/LanguageExtensions.html#objective-c-features.

Why is instancetype useful? Consider the following code:

NSDictionary *d = [NSArray arrayWithObjects:@(1), @(2), nil];
NSLog(@"%i", d.count);

While this is clearly incorrect, the compiler would historically do absolutely nothing to inform you of the error. Try it for yourself if you still have a copy of Xcode 4.6 lying around on a floppy somewhere. You’ll notice that no warning is generated, even though the code is clearly wrong! The code will even run without complaint as both NSDictionary and NSArray instances respond to count.

The reason this works at runtime is thanks to the powerful, dynamic nature of Objective-C. The type is purely a guide to the compiler. The count method is looked up at runtime on whatever class the dictionary variable happens to be. In this case, the count method exists, so the compiler believes all is well. However, this may come back to bite you later if you added code that uses another method that NSArray doesn’t have in common with NSDictionary, such as objectAtIndex:. At first, it wouldn’t be clear exactly where the issue lies.

But why does the compiler not figure out that the instance returned by +[NSArray arrayWithObjects:] is not an instance of NSDictionary? Well, that’s because its method signature is as follows:

+ (id)arrayWithObjects:(id)firstObj, ...;

Notice the return type is id. The id type is an umbrella type meaning any Objective-C class; it doesn’t even have to be a subclass of NSObject. It literally has no type information other than the fact it’s an instance of an Objective-C class. For this to be useful, the compiler doesn’t bother warning you when you implicitly cast id to a concrete type, such as NSDictionary* in the example above. If it did generate a warning, the id type would be largely useless.

But why is the return type of that method id in the first place? That’s so you can successfully subclass the method and still use it without issue. To demonstrate why, consider the following subclass of NSArray:

@interface MyArray : NSArray
@end

Now consider the use of your new subclass in the code below:

MyArray *array = [MyArray arrayWithObjects:@(1), @(2), nil];

Ah — now you see why the return type of arrayWithObjects: must be id. If it were NSArray*, then subclasses would require to be cast to the necessary class. This is where the new instancetype return type comes in.
If you look at the header file for NSArray in the iOS 7.0 SDK, you’ll notice the method signature for this method has changed to the following:

+ (instancetype)arrayWithObjects:(id)firstObj, ...;

The only difference is the return type. This new return type provides a hint to the compiler that the return value will be an instance of the class the method is called on. So when arrayWithObjects: is called on NSArray, the return type is inferred to be NSArray*; if called on MyArray, the return type is inferred to be MyArray* and so on.

This works around around the problem with the umbrella id type, while maintaining the ability to subclass successfully. If you compile the original code in Xcode 5, you’ll now see the following warning:

warning: incompatible pointer types initializing 'NSDictionary *' with an expression of type 'NSArray *' [-Wincompatible-pointer-types]
  NSDictionary *d = [NSArray arrayWithObjects:@(1), @(2), nil];
                ^    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

w00t — now that’s helpful! You now have the opportunity to fix the problem before it turns into a crash later down the line.

Initializers are also candidates for using this new return type. The compiler has warned you for some time now if you set the return type of an initializer to that of an incompatible type. But presumably it’s just implicitly converting the id return type to instancetype under the hood. You should still use instancetype for initializers though, because it’s better to be explicit for habit’s sake.

Strive to use instancetype as much as possible going forward; it’s become a standard for Apple — and you never know when it will save you some painful debugging time later on.

New Foundations

The remaining part of this article is dedicated to various new bits of functionality in Foundation, the core framework of all Objective-C development. It’s hard to develop Objective-C applications without Foundation, as all iOS apps require its use. Finding new gems in Foundation is a big part of the fun of getting your hands on a new release of the iOS SDK!

One of the major improvements in this release of Foundation lies in networking; so much, in fact, that there’s an entire chapter dedicated to it in iOS 7 by Tutorials. This is pretty important stuff, so we’re going to release an abbreviated version of the chapter later on in the iOS 7 Feast to help everyone get up to speed with it.

The rest of this section outlines other interesting additions and changes to Foundation.

NSArray

Trying to retrieve an object from an instance of NSArray will throw an exception if the index you supply is beyond the length of the array. You’ll also often find a need to access the first and last objects of an array, such as when you’re using a mutable array to hold a queue. In a first-in-first-out (FIFO) queue you pop objects from the front of the array, and in a first-in-last-out (FILO) you pop objects from the end of the array.

However, when you retrieve the first or last objects from an array, you must always ensure that you’re not going to read past the end of the array, which could easily occur if the array were empty. This leads to a lot of tedious code to ensure a call to objectAtIndex: won’t throw an exception, like so:

NSMutableArray *queue = [NSMutableArray new];

// ...

if (queue.count > 0) {
    id firstObject = [queue objectAtIndex:0];
    // Use firstObject
}

// ...

if (queue.count > 0) {
    id lastObject = [queue objectAtIndex:(queue.count - 1)];
    // Use lastObject
}

In the case of retrieving the last object, you’ve always had the option of using the following method of NSArray:

- (id)lastObject;

Objective-C developers are the rejoicing the world over, as for the first time they have access to an equivalent method to retrieve the first object of the array:

- (id)firstObject;

This single, simple method turns out to be extremely useful. No longer do you have to account for an array being empty. If you’ve ever felt the sting of a crash because of an out of bounds exception then you’ll definitely see this as a welcome addition.

Note: If you look at the header for NSArray carefully, you’ll see that firstObject has actually been around since iOS 4.0 but it wasn’t made public until iOS 7. Therefore you could have accessed the method prior to iOS 7, but that would have required declaring the firstObject selector in one of your own header files to inform the compiler that it does in fact exist. That’s certainly not a recommended approach, so it’s good that Apple finally brought it out of hiding.

The previous code snippet can now be rewritten to use these two utility methods and forego the length checks, as illustrated below:

NSMutableArray *queue = [NSMutableArray new];
// ...

id firstObject = [queue firstObject];
// Use firstObject

id lastObject = [queue lastObject];
// Use lastObject

Contributors

Over 300 content creators. Join our team.