Lessons from iOS development #4: Let’s forget this part 2

A few weeks ago I blogged about tactics you can use to reduce memory usage in your iOS app however I came across a neat class that can make your life a lot easier.

NSCache is a very simple class that is little more than an NSMutableDictionary except that it will automatically clear objects when memory usage becomes high or they haven’t been used recently. For an app heavy on file/network IO this is perfect.

Keep Calm’s main view is a grid of images. Usually these are loaded from a thumbnail that is automatically generated in the background every time the user updates their poster. This works fine, however it can take up to 10ms to load a picture, so when scrolling (especially on iPad where up to four pictures are presented in one row) to a new row there is a slight jitter even though the images are loaded asynchronously.

So then I added an NSCache to store all the thumbnails and suddenly scrolling became completely smooth because the thumbnails were only loaded when they needed to be (if it all, one would expect that a tablet with 1GB of RAM could handle ~30 images happily). Another benefit of NSCache is that you can limit the total ‘cost’ of all the items (the cost of an item is a relative number you can dictate).

Obviously it can be used for things other than just images, but if you are working on any kind of app that presents images in a grid you can save yourself a lot of time by using NSCache.

Advertisements

Lessons from iOS Development #3: Let’s forget this

I’ve blogged previously about memory management with ARC in iOS but I thought I would cover some different aspects that aren’t related to retain/release cycles but focus on when stuff really needs to be kept in memory. I’ve split this post into a few different sections based on different areas of iOS development.

Core Data

Core Data is really great (except if you’re using it with iCloud) as an ORM and I’ve found it to be a worthy replacement to SQLite on iOS, albiet with some limitations. I’ve generally found that it uses a little more memory than SQLite does for the same/similar tasks, but keeping a context and reusing it throughout your application (except in different threads) generally works quite well.

A major benefit of Core Data is NSFetchedResultsController. If you wanted to fetch an object from a Core Data store normally you might do something like the following:

NSFetchRequest *fetchRequest = [NSFetchRequest new];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Object" inManagedObjectContext:self.context];
[fetchRequest setEntity:entity];
NSArray * results = [self.context executeFetchRequest:fetchRequest error:nil];

If you’ve only got a few attributes on your object with few (if any) relationships and there are only going to be a small number of them, then this approach works fine. But what if they’re bigger than you think they are? What if each object has an image attached to it and there are 1000 objects? Your users may surprise you.

Keeping a huge number of objects in memory at any one time is never going to be a sensible idea, so thankfully Apple has provided us with an alternative. I shall not explain all the details of how NSFetchedResultsController works because Ray Wenderlich has a pretty good tutorial on it but it means that you only have to fetch an object when you actually need it, and after that it disappears, which can massively reduce memory load and massively increase table/collection view performance.

Images

Apple generally encourages developers that they ought to use separate image files for iPad, iPhone, Retina and Non-Retina devices (although, based on normal release cycles, I suspect there will be no non-Retina devices available by the end of this year and no non-Retina devices receiving OS updates by the end of next year) and it is incredibly important that you only keep an image in memory that is the highest resolution that you need.

For example, say you are working on an app that does photo manipulation and users can load photos from their camera. On an iPhone, these images are going to be eight megapixels (it’s nearer 7.9 million pixels) however the iPhone screen only has 0.7 million pixels – less than a tenth. The full image doesn’t need to be kept in memory, so it be resized before being presented in the app. However, if the user needs to export their edited photo they may wish to do so at a higher resolution, so the the original photo should be saved to disk temporarily.

If you want some decent code that resizes UIImages accurately (and quickly) I recommend the UIImage categories described in this blog post. In general it is a lot quicker to use Quartz2D for a task like this because converting the image to a Core Image type can be slow, and converting it back can be slower.

Although not entirely related, always be wary of which format you are using for your images. Apple recommends PNGs everywhere, but JPEG often provides much better compression for photos.

Strings and big data from the web

I’ve found that storing large NSStrings (about a million characters large) to be generally stupid because a) it slows iOS devices down big time and b) some NSString functions become incredibly slow. It is therefore wise to read a file line by line if you are only processing a small chunk at a time, for example. Alternatively use SQLite if you can so that text can be split up.

Also be aware of loading data from the web. If you can absolutely guarantee its source and you know how big it is, the download it with NSData and keep it in memory. On the other hand, can you absolutely guarantee that what you are downloading is small and won’t fill up the RAM (which can be an issue on mobile devices, especially older ones)? Use other functions instead of loading it one go.

Lessons from iOS dev #2: Delegates are good, blocks are better

One of the core fundamentals of Objective-C is protocols and delegates. Almost every single Objective-C app is likely to use them in some way or another because for many years they have been the ubiquitous way to communicate universally between classes. Whilst they are highly useful and I use them pretty much everywhere, they aren’t always the best solution to the problem.

Keep Calm has two main view controllers (the grid and editing view) and each of them is a delegate for about forty odd different protocols. At least thirty of them are protocols I’ve written myself, rather than Apple ones such as UICollectionViewDataSource. I’ve done a reasonably good job at using #pragma – marks to organise the various methods in the delegates, but the classes are beginning to feel a little bloated.

The new alternative is to use blocks instead. Blocks are essentially an extension of C function pointers. The main benefit is that it massively reduces the amount of code you have to write complete a task:

With a protocol and delegate

@protocol MyClassDelegate

-(void)doSomethingInDelegate;

@end

@interface MyClass : NSObject

@property id<MyClassDelegate> delegate;

-(void)doSomething

@end

@implementation MyClass

-(void)doSomething
{
    [self.delegate doSomethingInDelegate];
}

@end

@interface MyOtherClass<MyClassDelegate> : NSObject
...
@end

@implementation MyOtherClass

-(void)doSomethingInDelegate
{
    NSLog(@"Doing something in delegate");
}

@end

With a block

@interface MyClass : NSObject

-(void)doSomething:(void (^)(void))block;

@end

@implementation MyClass

-(void)doSomething:(void (^)(void))block
{
    block();
}

@end

@implementation MyOtherClass

-(void)begin
{
    [self.myClass doSomething:^(){
        NSLog(@"Doing something in other class");
    }];
}

@end

Obviously the latter example is much shorter and the code is a little easier to read (once you get used to the syntax of blocks). Apple is beginning to use blocks across the Cocoa and Cocoa Touch frameworks for everything from fast iteration to sorting to completion handlers.

There are a lot of cases where blocks aren’t appropriate though – I wouldn’t make a table data source with blocks, for example, because the number of blocks would become ridiculous. In my mind they are best for short snippets of code where they are replacing a protocol that one uses a maximum of two functions. Another disadvantage is that blocks inside blocks produces ridiculously ugly code. Even with only three blocks inside one another my code is comfortably running off the screen in Xcode, which is a pain.

I’ve also got mixed views on how reusable the code produced by blocks is. You are effectively defining functions within functions, which makes it harder to take the source and use it elsewhere because you have to write the block syntax into regular Objective-C function syntax.

Blocks are very useful and its worth learning about them from Apple’s documentation. I’ve only touched on some of the things that you can do with them and I am sure that in the future Apple will carry on introducing them across Cocoa.

Lessons from iOS dev #1: Threads solve nothing

A bit of prehistory: I first wrote Keep Calm on Android about a year ago and the whole app run on the UI thread. All posters were generated, saved, edited, etc on one thread (unless the system did it on another thread for me, which it generally didn’t). I then learnt the quirks of concurrency on Android and I thought great, now the app should run a little smoother. By last summer when I was writing the first version of Keep Calm on iOS I had fallen in love with concurrency, and even the version currently in the store uses about thirty odd threads (hence why it is so slow, and I’m pushing an update).

Firstly, concurrency is absolutely vital in making a good app. You can’t do network communications, heavy file IO or complex processing on the UI thread because it will begin to become unresposive.

Secondly, don’t use performSelectorInBackground: when you need to do something more than once in the background. It creates a whole new thread which is often completely unnecessary. The superior alternative is to use NSOperationQueue. My current pattern is to use a single background queue across the application because creating additional queues is unnecessary: one NSOperationQueue doesn’t represent a single thread (unless you limit it to) and can run several operations concurrently, so there is no real need to create new queues.

Thirdly, it is far too easy to over-use concurrency. I found myself doing loads of UI operations on another thread which eventually meant I was drawing things about three times and just wasting time and processing power.

I have a new rule for working with concurrency: the view and controller layers (I use MVC, as one should with iOS development) should operate on a single main thread however the controller can instantiate tasks on the Model layer either on the main thread or on my separate NSOperationQueue. My reasons for this are because it reduces complexity, is easy to understand and actually ensures that the app ends up being pretty quick. Obviously there are some tasks that the controller may ‘do’ in a separate thread, however I consider them to be Model tasks.

Lessons from iOS dev #0: Intro

I began doing iOS development around a year ago and since then I’ve gradually improved, having learnt Objective-C entirely from scratch. In this past year I’ve published a few iOS apps (most notably Keep Calm which has had nearly 2M downloads) and I’m gradually moving towards some larger projects, hopefully some games, this summer.

A lot of what I’m going to write over the course of these posts is solely from my own experience. Not all of it will be correct but I’m going to try and focus on things that there isn’t as much documentation on that an iOS developer starting out might not realise. I’ve also learnt a lot of neat tricks as well, so I’ll probably cover those.

Aside from benfitting other people, it also has the advantage that I’m getting stuff out of my head and getting it written down because I’m bound to forget it :).