25 iOS App Performance Tips & Tricks

This article gathers together 25 tips and tricks that you can use to improve the performance of your apps, in the form of a handy checklist. By .

4.8 (6) · 1 Review

Save for later
Share

Follow these tips to improve your app performance!

Follow these tips to improve your app performance!

Follow these tips to improve your app performance!

This is a post by iOS Tutorial Team Member Marcelo Fabri, an iOS developer working at Movile. Check out his personal website or find him on Twitter or on .

When developing an iOS app, it’s critical that your app has good performance. Your users expect it, and it will hurt your reviews if your app appears unresponsive or slow.

However, due to the limitations of iOS devices this can sometimes be quite hard to get working quite right. There’s a lot to keep in mind during development, and it’s easy to forget the performance impact of your decisions.

That is exactly why I wrote this article! This article gathers together 25 tips and tricks that you can use to improve the performance of your apps, in the form of a handy checklist.

So keep reading to give your future apps a nice boost!

Note: Before optimizing any code, make sure there’s a problem to be solved! Don’t get sucked into the mistake of “pre-optimizing” your code. Use Instruments frequently to profile your code and uncover any areas that need improvement. Matt Galloway wrote a great tutorial about using Instruments to optimize your code.

Also, keep in mind that some of the tips offered in this article offered are trade-offs; the suggested improvements will make your code faster or more efficient, but they may require a lot of work to implement, or make your code more complicated, so choose wisely!

Note: Before optimizing any code, make sure there’s a problem to be solved! Don’t get sucked into the mistake of “pre-optimizing” your code. Use Instruments frequently to profile your code and uncover any areas that need improvement. Matt Galloway wrote a great tutorial about using Instruments to optimize your code.

Also, keep in mind that some of the tips offered in this article offered are trade-offs; the suggested improvements will make your code faster or more efficient, but they may require a lot of work to implement, or make your code more complicated, so choose wisely!

Table of Contents

The tips below are categorized into three different levels – beginner, intermediate and advanced:

Beginner

These are tips that you’ll always want to implement in any app you develop.

  1. Use ARC to Manage Memory
  2. Use a reuseIdentifier Where Appropriate
  3. Set Views as Opaque When Possible
  4. Avoid Fat XIBs
  5. Don’t Block the Main Thread
  6. Size Images to Image Views
  7. Choose the Correct Collection
  8. Enable GZIP Compression

Intermediate

These are tips you should use when you run into slightly more complicated issues.

  1. Reuse and Lazy Load Views
  2. Cache, Cache, Cache
  3. Consider Drawing
  4. Handle Memory Warnings
  5. Reuse Expensive Objects
  6. Use Sprite Sheets
  7. Avoid Re-Processing Data
  8. Choose the Right Data Format
  9. Set Background Images Appropriately
  10. Reduce Your Web Footprint
  11. Set the Shadow Path
  12. Optimize Your Table Views
  13. Choose Correct Data Storage Option

Advanced

These are tips you should use only when you’re positive they’ll fix the issue, and you feel comfortable using them.

  1. Speed up Launch Time
  2. Use Autorelease Pool
  3. Cache Images – Or Not
  4. Avoid Date Formatters Where Possible

Without further ado, let’s get into the tips!

Beginner Performance Improvements

This section is dedicated to basic changes that can improve your app’s performance. But developers of all levels will still benefit from this quick checklist of items that are still sometimes overlooked.

 
1) Use ARC to Manage Memory

ARC was released with iOS 5 and it eliminates the most common kind of memory leaks – the forgetful ones.

ARC stands for “Automatic Reference Counting”, and it automatically manages the retain/release cycles in your code, so you don’t have to do it manually.

The code block below shows some common code that you might use to create a view:

UIView *view = [[UIView alloc] init];
// ...
[self.view addSubview:view];
[view release];

It’s tremendously easy to forget the release call at the end of this code block. ARC does it for you, automatically and under-the-hood.

In addition to helping you avoid memory leaks, ARC can also improve your performance, by making sure that objects are deallocated as soon as they are no longer needed. These days, you should always use ARC in your projects!

Here are a few great resources to learn more about ARC:

It’s worth noting that ARC doesn’t eliminate all memory leaks. You can still have memory leaks, but they’ll mainly be due to blocks, retain cycles, poorly managed CoreFoundation objects (and C structures in general), or just really poorly written code.

There’s a very good blog post that details some of the issues that ARC can’t fix — and how to deal with them.

 
2) Use a reuseIdentifier Where Appropriate

Use a reuseIdentifier Where Appropriate.

Use a reuseIdentifier Where Appropriate.

Use a reuseIdentifier Where Appropriate.

A common mistake in app development is not setting the correct reuseIdentifier for UITableViewCells, for UICollectionViewCells, or even UITableViewHeaderFooterViews.

For maximum performance, a table view’€™s data source should generally reuse UITableViewCell objects when it assigns cells to rows in tableView:cellForRowAtIndexPath:. A table view maintains a queue or list of UITableViewCell objects that the data source has marked for reuse.

What happens if you don’t use a reuseIdentifier?

If you don’t, your table view will configure a brand-new cell each time a row is displayed. This is an expensive operation and will definitely affect the scrolling performance of your app.

Since the introduction of iOS 6, you’re expected to use reuseIdentifiers for header and footer views, as well as UICollectionView’s cells and supplementary views.

To use reuseIdentifiers, call this method from your data source object when asked to provide a new cell for the table view:

static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

This method dequeues an existing cell if one is available, or creates a new one if necessary using the previously registered nib file or class. If no cell is available for reuse, and you did not register a class or nib file, this method returns nil.

 
3) Set Views as Opaque When Possible

Set Views as Opaque When Possible.

Set Views as Opaque When Possible.

Set Views as Opaque When Possible.

If you have opaque views — that is, views that have no transparency defined — you should set their opaque property to YES.

Why? This will allow the system to draw your views in an optimal manner. It’s a simple property that can be set in both Interface Builder and code.

The Apple documentation has this to say about setting the opaque property for images:

This property provides a hint to the drawing system as to how it should treat the view. If set to YES, the drawing system treats the view as fully opaque, which allows the drawing system to optimize some drawing operations and improve performance. If set to NO, the drawing system composites the view normally with other content. The default value of this property is YES.

This property provides a hint to the drawing system as to how it should treat the view. If set to YES, the drawing system treats the view as fully opaque, which allows the drawing system to optimize some drawing operations and improve performance. If set to NO, the drawing system composites the view normally with other content. The default value of this property is YES.

On relatively static screens, setting the opaque property won’t be a big deal. However, if your view is embedded in a scroll view, or is part of a complex animation, not setting this property will definitely impact the performance of your app!

You can also use the Debug\Color Blended Layers option in the simulator to see visually what views are not set as opaque. Your goal should be to make as many of your views opaque as possible!

 
4) Avoid Fat XIBs

Avoid Fat XIBs.

Avoid Fat XIBs.

Avoid Fat XIBs.

Storyboards, introduced in iOS 5, are quickly replacing XIBs. However, XIBs are still useful in some cases. If you need to target pre-iOS 5 devices, or you have a custom reusable view, then you can’t really avoid them.

If you’re forced into using XIBs, make them as uncomplicated as possible. Try to create one XIB per view controller, and if possible, break out a view controller’s view hierarchy into separate XIBs.

Note that when you load a XIB into memory, all of its contents are loaded into memory, including any images. If you have a view you’re not using immediately, then you’re wasting precious memory. It’s worth noting that this won’t happen with storyboards, since a storyboard will only instantiate a view controller when it’s needed.

When you load a XIB, any image files are cached, along with sound files if you’re developing for OS X. Apple’s documentation has this to say:

When you load a nib file that contains references to image or sound resources, the nib-loading code reads the actual image or sound file into memory and and caches it. In OS X, image and sound resources are stored in named caches so that you can access them later if needed. In iOS, only image resources are stored in named caches. To access images, you use the imageNamed: method of NSImage or UIImage, depending on your platform.

When you load a nib file that contains references to image or sound resources, the nib-loading code reads the actual image or sound file into memory and and caches it. In OS X, image and sound resources are stored in named caches so that you can access them later if needed. In iOS, only image resources are stored in named caches. To access images, you use the imageNamed: method of NSImage or UIImage, depending on your platform.

Apparently, this also happens when using storyboards; however, I wasn’t able to find anything supporting this claim. If you know anything about this behavior, please drop me a line!

Want to learn more about storyboards? Check out Matthijs Hollemans’ Beginning Storyboards in iOS 5 Part 1 and Part 2.

 
5) Don’t Block the Main Thread

Don’t Block the Main Thread.

Don't Block the Main Thread.

Don’t Block the Main Thread.

You should never do any heavy lifting on the main thread. This is because UIKit does all of its own work on the main thread, such as drawing, managing touches, and responding to input.

The risk of doing all of your app’s work on the main thread is that if your code does block this thread, your app will appear unresponsive. That’s a quick route to one-star reviews on the App Store! :]

Most cases of blocking the main thread occur when your app is performing an I/O operation which involves any task that needs to read or write from an external source, such as the disk or the network.

You can perform network tasks asynchronously by using this method on NSURLConnection:

+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler

or by using a third party framework such as AFNetworking.

If you’re doing any other kind of expensive operation (such as performing a time-intensive computation or reading/writing to the disk) then use Grand Central Dispatch, or NSOperations and NSOperationQueues.

The template for using GCD looks like the code below:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // switch to a background thread and perform your expensive operation
    
    dispatch_async(dispatch_get_main_queue(), ^{
        // switch back to the main thread to update your UI

    });
});

See how there’s a nested dispatch_async inside the first one? That’s because any UIKit related code needs to be executed on the main thread.

Curious about the finer details of NSOperation or GCD? Take a look at Ray Wenderlich’s Multithreading and Grand Central Dispatch on iOS for Beginners tutorial, as well as Soheil Azarpour’s How To Use NSOperations and NSOperationQueues tutorial.

 
6) Size Images to Image Views

Size Images to Image Views.

Size Images to Image Views.

Size Images to Image Views.

If you’re displaying an image from the app’s bundle in a UIImageView, make sure that the image and the UIImageView are same size. Scaling images on the fly can be expensive, especially if your UIImageView is embedded in a UIScrollView.

If the image is downloaded from a remote service, sometimes you don’t have control over the size, or you might not be able to scale it on the server prior to downloading. In cases like these, you can scale the image manually once you’ve finish downloading it — preferably on a background thread! — and then use the resized image in your UIImageView.

 
7) Choose the Correct Collection

Choose the Correct Collection.

Choose the Correct Collection.

Choose the Correct Collection.

Learning to use the most appropriate class or object for the task at hand is fundamental to writing efficient code. This is especially true when dealing with collections.

Happily, there’s a document named Collections Programming Topics on Apple’s Developer Library that explains in detail the differences between the available classes, as well as which situations suit each class. It’s a must read document for anyone looking to work with collections.

TLDR? Here’s a quick synopsis of the most common collection types:

  • Arrays: Ordered list of values. Quick lookup by index, slow to lookup by value, slow to insert/delete.
  • Dictionaries: Store key/value pairs. Quick lookups by key.
  • Sets: Unordered list of values. Quick lookup by value, quick to insert/delete.

 

8) Enable GZIP Compression

Enable GZIP compression.

Enable GZIP compression.

Enable GZIP compression.

A significant and growing number of apps rely on external data from remote servers or other external APIs. At some point you’ll be developing an app that downloads data in XML, JSON, HTML or some other text format.

The problem is that the network condition cannot be relied upon when it comes to mobile devices. A user can be on an EDGE network one minute, and the a 3G network the next. Whatever the scenario, you don’t want to keep your user waiting!

One option to reduce the file size and speed up the download of network-based resources is by enabling GZIP compression on both your server and on your client. This is especially useful for text-based data, which has a high potential ratio for compression.

The good news is that iOS already supports GZIP compression by default if you’re using NSURLConnection, or a framework built on top of it such as AFNetworking. Even more good news is that some cloud providers, such as Google App Engine already send compressed responses.

There’s a great article about GZIP compression which explains how to enable it on your Apache or IIS server.