NSCoding is a fantastically simple and convenient way to store data on iOS or Mac OS by turning your model objects directly into a file and then loading it back into memory again, without needing to write any file parsing and serialization logic. To save any object to a data file (assuming that it implements the NSCoding protocol), you can just do this:
Foo *someFoo = [[Foo alloc] init];
[NSKeyedArchiver archiveRootObject:someFoo toFile:someFile];
And to load it again later:
Foo *someFoo = [NSKeyedUnarchiver unarchiveObjectWithFile:someFile];
That’s fine for resources that are compiled into your app (such as nib files, which use NSCoding under the hood), but the problem with using NSCoding for reading and writing user data files is that, because you are encoding whole classes in a file, you are implicitly giving that file (with potentially unknown origin) permission to instantiate classes inside your app.
Unless you’ve been living under a rock, you’ll have probably heard of a little iOS game called Flappy Bird.
Whilst users went nuts over it, the iOS developer community’s response to Flappy Bird was a bit less enthusiastic; many criticised it for its poor implementation and relative crudeness. But though several developers declared that they could have built Flappy Bird in an hour, others quietly confessed that, although they could see it was simplistic, they actually wouldn’t have the first clue where to begin with writing a game for iOS – even one as simple as Flappy Bird.
Games are a hugely popular genre of app, but making games is a somewhat different process to traditional application development. There are dozens of ways to build games on iOS, from cross-platform tools such as Unity, to 3rd party frameworks such Cocos2D, to built-in APIs such as SpriteKit and GLKit. Most app developers have heard of these, but many have never have tried using them.
In this tutorial, we’ll demystify gaming on iOS by building
Flappy Bird Floaty Balloon in a couple of hours, using ordinary UIKit classes that any app developer should already be familiar with. UIKit is not designed for gaming, but as we will demonstrate, it’s perfectly suitable for building a simple game such as this.
A common task in Cocoa programming is to loop through a collection of objects (e.g. an NSArray, NSSet or NSDictionary). This seemingly simple problem has a wide variety of solutions, many of them with subtle performance considerations.
The Need for Speed
First, a disclaimer: The raw speed of one Objective-C method versus another is one of the last things you should worry about when programming – the difference is unlikely to be dramatic enough to matter compared with other, more important considerations, such as code clarity and readability.
But not prioritising speed is no excuse for not understanding it. You should always learn the performance implications of the code you are writing so that on the rare occasions when it does matter, you’ll know what to do.
Also, in the case of looping, much of the time it doesn’t matter which technique you choose from a readability or clarity perspective, so you might as well choose the fastest. There’s no point writing code that is slower than it needs to be.
With that in mind, here are the options:
Deprecated APIs, as you may know, are methods or classes that are outdated and will eventually be removed. Apple deprecates APIs when they introduce a superior replacement, usually because they want to take advantage of new hardware, OS or language features (e.g. blocks) that were’t around when the original API was conceived.
Whenever Apple adds new methods, they suffix the method declarations with a special macro that makes it clear which versions of iOS support them. For example, for UIViewController, Apple added a new way to present modal controllers that uses a block callback. The method declaration looks like this:
- (void)presentViewController:(UIViewController *)viewControllerToPresent
animated: (BOOL)flag completion:(void (^)(void))completion NS_AVAILABLE_IOS(5_0);
As you may be aware, every object in Objective-C has a description method that returns a string. This is similar to the toString method in languages like Java and C#.
The purpose of the description method is not really just to convert objects to strings; NSNumber has a stringValue method for that purpose, NSArray has componentsJoinedByString: and NSDate can be “stringified” using an NSDateFormatter, etc. The description method’s main purpose is for debugging.
You typically use the description method in one of two ways. Either in code, using NSLog, as follows:
In Object Serialization With NSCoding we talked about how your can use the NSKeyedArchiver and the NSCoding protocol to easily save your custom model objects as Binary Plists for later retrieval. As a reminder, here is how we would implement NSCoding for a simple class “Foo”, with three properties:
@interface Foo : NSObject <NSCoding>
@property (nonatomic, assign) NSInteger property1;
@property (nonatomic, assign) BOOL property2;
@property (nonatomic, copy) NSString *property3;
- (id)initWithCoder:(NSCoder *)coder
if ((self = [super init]))
// Decode the property values by key,
// and assign them to the correct ivars
_property1 = [coder decodeIntegerForKey:@"property1"];
_property2 = [coder decodeBoolForKey:@"property2"];
_property3 = [coder decodeObjectForKey:@"property3"];
- (void)encodeWithCoder:(NSCoder *)coder
// Encode our ivars using string keys
[coder encodeInteger:_property1 forKey:@"property1"];
[coder encodeBool:_property2 forKey:@"property2"];
[coder encodeObject:_property3 forKey:@"property3"];
NSCoding eliminates a lot of the complexity of saving objects by automatically handling circular references, duplicate objects, etc. But you still have to write those pesky initWithCoder: and encodeWithCoder: methods for every class. That’s kinds of a drag. Implementing NSCoding can involve a lot of boilerplate, especially if your object has a lot of properties to encode. Is there anything we can do about that?
Categories are a cool feature of Objective-C that allow you to extend existing classes with extra methods. This helps you to write really clean code by adding utility methods directly to the classes they relate to, so they can be called in a natural, object-oriented way.
A typical example of a category method is given below. Here we add an isNumeric method to NSString: