from http://www.raywenderlich.com/31166/25-ios-app-performance-tips-tricks
This is a post by iOS Tutorial Team Member Marcelo Fabri, an iOS developer working at Movile. Check out his personal websiteor follow him on Twitter.
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!
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.
- Use ARC to Manage Memory
- Use a reuseIdentifier Where Appropriate
- Set Views as Opaque When Possible
- Avoid Fat XIBs
- Don’t Block the Main Thread
- Size Images to Image Views
- Choose the Correct Collection
- Enable GZIP Compression
Intermediate
These are tips you should use when you run into slightly more complicated issues.
- Reuse and Lazy Load Views
- Cache, Cache, Cache
- Consider Drawing
- Handle Memory Warnings
- Reuse Expensive Objects
- Use Sprite Sheets
- Avoid Re-Processing Data
- Choose the Right Data Format
- Set Background Images Appropriately
- Reduce Your Web Footprint
- Set the Shadow Path
- Optimize Your Table Views
- 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.
- Speed up Launch Time
- Use Autorelease Pool
- Cache Images – Or Not
- 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.
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:
- Apple’s official documentation
- Matthijs Hollemans’s Beginning ARC in iOS Tutorial
- Tony Dahbura’s How To Enable ARC in a Cocos2D 2.X Project
- If you still aren’t convinced of the benefits of ARC, check out this article on eight myths about ARC to really convince you why you should be using it!
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
![](http://dl.iteye.com/upload/attachment/0082/8183/10dec39a-65ec-36b2-b4f4-1379557fafa9.png)
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
![](http://dl.iteye.com/upload/attachment/0082/8165/933e4e2f-f324-3f04-87e1-fdcdfc875ce9.png)
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.
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!
![](http://dl.iteye.com/upload/attachment/0082/8163/ead55a07-cd1c-36a3-b885-82617245bb2c.png)
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.
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 1and Part 2.
5) Don’t Block the Main Thread
![](http://dl.iteye.com/upload/attachment/0082/8161/b7426c0d-affc-3101-b7f5-377bb02ac90f.jpg)
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.
![](http://dl.iteye.com/upload/attachment/0082/8167/5ccd2f66-ac96-36a2-a29d-e6348b654e80.png)
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
![](http://dl.iteye.com/upload/attachment/0082/8173/d00f9132-e333-3f7f-9b0c-541028f06a84.png)
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
![](http://dl.iteye.com/upload/attachment/0082/8171/e947943f-5cf9-3196-9821-908da5dc23a2.png)
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.
Intermediate Performance Improvements
Okay, so you’re pretty confident that you’ve hit all of the low-hanging fruit when it comes to optimizing your code. But sometimes there are solutions that aren’t quite as obvious, and depend heavily on how you structure and code your app. However, in the right context, they can be invaluable!
More views means more drawing; which ultimately means more CPU and memory overhead. This is especially true if your app embeds many views inside of a UIScrollView.
The trick to managing this is to mimic the behavior of UITableView and UICollectionView: don’t create all subviews at once. Instead, create your views as you need them, adding them to a reuse queue when you’re finished with them.
This way, you have only to configure your views when a scroll is performed, avoiding the allocation cost — which can be expensive.
The problem of timing the creation of views applies to other areas of your app as well. Take the situation where you need to present a view when the user taps a button. There are at least two approaches to this:
- Create the view when the screen is first loaded and hide it; then when you need it, show it.
- Do nothing until you need to show the view. Then, create the view and show it, all at once.
Each approach has its own pros and cons.
Using the first method, you consume more memory because you immediately create the view which holds on to that memory until it’s released. However, when the user does taps the button, your app will appear more responsive as it only needs to change the view’s visibility.
Taking the second approach will have the opposite effect; by creating the view only when it’s required, you consume less memory; however, the app won’t appear as responsive when the button is tapped.
A great rule of thumb when developing your app is to “cache what matters” — that is, things that are unlikely to change, but are accessed frequently.
What can you cache? Some candidates for caching are remote server responses, images, or even calculated values, such as UITableView row heights.
NSURLConnection already caches resources on disk or in memory according to the HTTP headers it processes. You can even create a NSURLRequest manually and make it load only cached values.
Here’s a great snippet to use whenever you need to create a NSURLRequest for an image that is unlikely to change:
+ (NSMutableURLRequest *)imageRequestWithURL:(NSURL *)url { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.cachePolicy = NSURLRequestReturnCacheDataElseLoad; // this will make sure the request always returns the cached image request.HTTPShouldHandleCookies = NO; request.HTTPShouldUsePipelining = YES; [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; return request; } |
Note that you can fetch a URL request via NSURLConnection, but AFNetworking can fetch it as well; that way you don’t need to change all your networking code because of this tip! :]
If want to know more about HTTP caching, NSURLCache, NSURLConnection and friends, be sure to take a look at the NSURLCache entry on NSHipster.
If you need to cache other things that don’t involve HTTP requests, then NSCache is your go-to guy.
NSCache looks and behaves like an NSDictionary, but it automatically removes its contents when the system needs to reclaim memory. Mattt Thompson wrote this amazing post on NSHipster about it.
Interested in learning more about HTTP caching? Google has a best-practices document on HTTP cachingthat is a recommended read.
![](http://dl.iteye.com/upload/attachment/0082/8169/9cfe5e3c-7d86-3528-9a64-01145addc3c9.jpg)
Consider Drawing.
There are several ways to make great-looking buttons in iOS. You can use full sized images, resizable images, or you could go the distance and draw it manually using CALayer, CoreGraphics or even OpenGL.
Of course, there’s different levels of complexity with each of these approaches, as well as differences in their performance. There’s an awesome post about iOS graphics performance herewhich is well worth a read. Andy Matuschak, who is a member of the UIKit team over at Apple, commented on the post, and there’s some great insight into the different approaches and their performance trade-offs.
The short story is that using pre-rendered images is faster, because iOS doesn’t have to create an image and draw shapes on it to finally draw into than screen (the image is already created!). The problem is that you need to put all those images in your app’s bundle, increasing its size. That’s why using resizable images is so great: you save space by removing “wasted” image space that iOS can repeat for you. You also don’t need to generate different images for different elements (e.g. buttons).
However, by using images you lose the ability to tweak your images by code, needing to regenerate them every and putting them into the app again and again. That can be a slow process. Another point is that if you have an animation or just a lot of images with slightly changes (they can have multiple overlay color, for example), you’ll have to embed a lot of images, growing the app’s bundle size.
To summarize, you need to think what’s most important to you: drawing performance or app’s size. Generally both are important, so you’ll use both approaches in the same project!
iOS notifies all running apps when system memory is running low. Here’s what the official Apple documentation says about handling the low memory warning:
If your app receives this warning, it must free up as much memory as possible. The best way to do this is to remove strong references to caches, image objects, and other data objects that can be recreated later.
Fortunately, UIKit provides several ways to receive these low-memory warnings, including the following:
- Implement the applicationDidReceiveMemoryWarning: method of your app delegate.
- Override didReceiveMemoryWarning in your custom UIViewController subclass.
- Register to receive the UIApplicationDidReceiveMemoryWarningNotification notification.
Upon receiving any of these warnings, your handler method should respond by immediately freeing up any unnecessary memory.
For example, the default behavior of UIViewController is to purge its view if that view is not currently visible; subclasses can supplement the default behavior of their parent class by purging additional data structures. An app that maintains a cache of images might respond by releasing any images that are not currently on-screen.
It’s very important to release all memory possible once you receive a memory warning. Otherwise, you run the risk of having your app killed by the system.
However, be careful when you start culling objects to free up memory, as you’ll need to make sure they can be recreated later. Be sure to use the Simulate memory warning feature on the iOS simulator to test this condition while you are developing your app!
Some objects are very slow to initialize — NSDateFormatter and NSCalendar are two examples. However, you can’t always avoid using them, such as when parsing dates from a JSON/XML response.
To avoid performance bottlenecks when working with these objects, try to reuse these objects if at all possible. You can do this by either adding a property to your class, or by creating a static variable.
Note that if you choose the second approach, the object will remain in memory while your app is running, much like a singleton.
The code below demonstrates making a property that lazy-loads a date formatter. The first time it is called, it creates a new date formatter. Future calls will just return the already created instance:
// in your .h or inside a class extension @property (nonatomic, strong) NSDateFormatter *formatter; // inside the implementation (.m) // When you need, just use self.formatter - (NSDateFormatter *)formatter { if (! _formatter) { _formatter = [[NSDateFormatter alloc] init]; _formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; // twitter date format } return _formatter; } |
Also remember that setting a NSDateFormatter’s date format is almost as slow as creating a new one! Therefore, if you frequently need to deal with multiple date formats in your app, your code may benefit from initially creating, and reusing, multiple NSDateFormatter objects.
![](http://dl.iteye.com/upload/attachment/0082/8177/1c3e4ddb-ec2e-39fe-880e-9c7e69241cad.jpg)
Use sprite sheets.
So you’re a game developer? Then sprite sheets are one of your best friends. Sprite sheets make drawing faster and can even consume less memory than standard screen drawing methods.
There are two awesome sprite sheet tutorials about sprite sheets on this site:
- How To Use Animations and Sprite Sheets in Cocos2D
- How to Create and Optimize Sprite Sheets in Cocos2D with Texture Packer and Pixel Formats
The second tutorial covers pixel formats in detail, which can have a measurable impact on a game’s performance.
If you’re not yet familar with sprite sheets, then a great introduction can be found in SpriteSheets – The Movie, Part 1and Part 2. The author of these videos is Andreas Löw, the creator of Texture Packer, one of the most popular tools for creating sprite sheets.
Besides using sprite sheets, several tips already covered here can be applied to games as well. For example, if your game has many sprites, such as enemies or projectiles in your standard shoot-em-up, then you can reuse sprites instead of recreating them every time.
Many apps make calls for data from remote servers to get information the app requires to function. This data usually comes across in JSON or XML format. It’s important to try and use the same data structure at both ends when requesting and receiving the data.
Why? Manipulating data in memory to fit your data structures can be expensive.
For example, if you need to display the data in a table view, it would be best to request and receive the data in an array format to avoid any intermediate manipulation of the data to make it fit the data structure that you’re using in your app.
Similarly, if your application depends on accessing specific values by their keys, then you’ll probably want to request and receive a key/value pair dictionary.
By getting the data in the right format the first time, you’ll avoid a lot of re-processing in your app to make the data fit your chosen structure.
16) Choose the Right Data Format
![](http://dl.iteye.com/upload/attachment/0082/8178/2cf04131-d0b9-3d02-9c4b-d146730de727.jpg)
Choose the right data format.
There are multiple ways you can transfer data to your app from a web service, but the most common two are JSON and XML. You want to make sure you choose the right one for your app.
JSON is faster to parse, and is generally smaller than XML, which means less data to transfer. And since iOS 5, there’s built-in JSON deserialization so it’s easy to use as well.
However, one advantage XML has is that if you use the SAXparsing method, you can work with XML data as you read it off the wire, instead of having to wait for the entire data to arrive before you parse it like JSON. This can give you increased performance and reduced memory consumption when you are dealing with very large sets of data.
17) Set Background Images Appropriately
Like many other things in iOS coding, there’s at least two different ways to place a background image on your view:
- You can set your view’s background color to a color created with UIColor’s colorWithPatternImage.
- You can add a UIImageView subview to the view.
If you have a full size background image, then you should definitely use a UIImageView because UIColor’s colorWithPatternImage was made to create small pattern images that will be repeated, and not large images size. Using UIImageView will save a lot of memory in this case.
// You could also achieve the same result in Interface Builder UIImageView *backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background"]]; [self.view addSubview:backgroundView]; |
However, if you plan to have a patterned image background, which uses smaller images which will be repeated or tiled to fill the background, you should go with UIColor’s colorWithPatternImage instead, as it’s faster to draw and won’t use a lot of memory in this case.
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background"]]; |
UIWebView is very useful. It’s a very easy to display web content, or even to create a visual aspect of your app that would be difficult with standard UIKit controls.
However, you may notice that the UIWebView component you can use in your apps is not as fast as the one which powers Apple’s Safari app. This is down to the restricted use of Webkit’s Nitro Engine, which featuresJIT compilation.
So to get the best performance, you’ll need to tweak your HTML a bit. The first thing you should do is get rid of as much Javascript as you can, which includes avoiding large frameworks such as jQuery. It can sometimes be a lot faster to work with vanilla Javascript instead of relying on frameworks to do the work for you.
Also follow the practice of loading your Javascript files asynchronously where possible – especially when they don’t directly impact the behavior of the page, such as analytics scripts.
And finally — always be aware of the images that you are using, and keep images right-sized for your purposes. As mentioned earlier in this tutorial, make use of sprite sheets wherever possible to conserve memory and improve speed.
For more information, be sure to take a look at WWDC 2012 session #601 – Optimizing Web Content in UIWebViews and Websites on iOS.
So you need to add a shadow to a view, or to a layer. How should you handle this?
Most developers would just add the QuartzCore framework to their project, and then add the following code:
#import <QuartzCore/QuartzCore.h> // Somewhere later ... UIView *view = [[UIView alloc] init]; // Setup the shadow ... view.layer.shadowOffset = CGSizeMake(-1.0f, 1.0f); view.layer.shadowRadius = 5.0f; view.layer.shadowOpacity = 0.6; |
Looks pretty easy, right?
The bad news is that there’s a problem with this approach. Core Animation has to do an offscreen pass to first determine the exact shape of your view before it can render the drop shadow, which is a fairly expensive operation.
The good news is that there’s an alternative that is much easier for the system to render: setting the shadow path!
view.layer.shadowPath = [[UIBezierPath bezierPathWithRect:view.bounds] CGPath]; |
By setting the shadow path, iOS won’t need to recalculate how it should draw the shadow all the time. Instead it’s going to use a pre-calculated path that you’re informing. The bad news is that depending on your view format, it may be harder to calculate the path by your own. Another problem is that you need to update the shadow path every time your view’s frame changes.
If you want to know more about this trick, Mark Pospesel wrote a great post about shadowPath.
Table views need to scroll quickly — when they don’t, users really notice the lag.
To keep your table views scrolling smoothly, ensure that you’ve implemented all of the suggestions below:
- Reuse cells by setting the correct reuseIdentifier.
- Make as many views opaque as possible, including the cell itself.
- Avoid gradients, image scaling, and offscreen drawing.
- Cache the height of any rows if they aren’t always the same.
- If the cell shows content that comes from the web, be sure to make those calls asynchronously and cache the responses.
- Use shadowPath to set up shadows.
- Reduce the number of subviews.
- Do as little work as possible in cellForRowAtIndexPath:. If you need to do some work, do it only once and cache the results.
- Use the appropriate data structure to hold the information you need. Different structures have different costs for different operations.
- Use rowHeight, sectionFooterHeight and sectionHeaderHeight to set constant heights instead of asking the delegate.
21) Choose Correct Data Storage Option
![](http://dl.iteye.com/upload/attachment/0082/8181/f8cb05fe-4a37-3a4b-bfcc-19cd13df00bf.jpg)
Choose Correct Data Storage Option.
What are your options when it comes to storing and reading large data sets?
You have several options, including:
- Store them using NSUserDefaults
- Save to a structured file in XML, JSON, or Plist format
- Archive using NSCoding
- Save to a local SQL database such as SQLite
- Use Core Data.
What’s the issue with NSUserDefaults? Although NSUserDefaults is nice and easy, it’s really only good if you have a very small amount of data to save (like what level you’re on, or whether sound is turned on and off). Once you start getting large amounts of data, other options are better.
Saving to a structured file can be problematic as well. Generally, you need to load the entire file into memory before you can parse it, which is an expensive operation. You could use SAX to process a XML file, but that’s a complex solution. As well, you’d end up having all objects loaded in memory — whether you want them there or not.
Okay, then, what about NSCoding? Unfortunately, it also reads and writes to a file, so it experiences the same problems as above.
Your best bet in this situation is to use SQLite or Core Data. By using these technologies, you can perform specific queries to only load the objects you need and avoid a brute-force searching approach to retrieve the data. In terms of performance, SQLite and Core Data are very similar.
The big difference between SQLite and Core Data is really about the general usage of each. Core Data represents an object graph model, while SQLite is just a regular DBMS. Usually Apple recommends that you go with Core Data, but if you have a particular reason you want to avoid it, you can go lower level to SQLite.
If you choose to use SQLite in your app, a handy library to use is FMDB which allows you to work with a SQLite database without having to delve into the SQLite C API.
Advanced Performance Tips
Looking for some elite ways to become a total code ninja? These advanced performance tips can be used when appropriate to make your apps run as efficiently as possible!
Launching your app quickly is very important, especially when the user launches for the first time. First impressions mean a lot for an app!
The biggest thing that you can do to ensure your app starts as quickly as possible is to perform as many asynchronous tasks as possible, such as network requests, database access, or parsing data.
As well, try to avoid fat XIBs, since they’re loaded on the main thread. But recall that storyboards don’t have this problem — so use them if you can!
Note: The watchdog doesn’t run while debugging with Xcode, so be sure to test your app with your device disconnected from Xcode while testing for launch performance.
NSAutoreleasePool is responsible for releasing the autoreleased objects within a block. Usually, it’s called automatically by UIKit. But there are some scenarios where may you need to create NSAutoreleasePools manually.
For example, if you create a lot of temporary objects in your code, you’ll note that memory usage increases until these objects are released. The problem is that this memory is only released after UIKit drains its autorelease pool, which means this memory is held much longer than necessary.
The good news is that you can avoid this by creating these temporary objects inside your own @autoreleasepool block, as shown in the code below:
NSArray *urls = <# An array of file URLs #>; for (NSURL *url in urls) { @autoreleasepool { NSError *error; NSString *fileContents = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error]; /* Process the string, creating and autoreleasing more objects. */ } } |
This releases all the autorelease objects at the end of each iteration.
You can read more about NSAutoreleasePool in Apple’s official documentation.
There are two common ways to load a UIImage from an app bundle. The first, and more common way to do it is using imageNamed. The second, and less common way, is with imageWithContentsOfFile.
Why are there two methods which effectively achieve the same thing?
imageNamed has the advantage of caching the image as it’s loaded. The documentation for imageNamedexplains it this way:
This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method loads the image data from the specified file, caches it, and then returns the resulting object.
Alternately, imageWithContentsOfFile simply loads the image with no caching.
The two methods are demonstrated in the code block below:
UIImage *img = [UIImage imageNamed:@"myImage"]; // caching // or UIImage *img = [UIImage imageWithContentsOfFile:@"myImage"]; // no caching |
When would you use one over the other?
If you’re loading a large image that will be used only once, there’s no need to cache the image. In this caseimageWithContentsOfFile will fit the bill nicely – this way, the OS doesn’t waste memory caching the image.
However, imageNamed is a much better choice for images that you’re going to be reusing in your app. This way, the OS saves time having to constantly load the image from disk.
25) Avoid Date Formatters Where Possible
If you have a lot of dates that you need to parse with NSDateFormatter, you need to tread carefully. As mentioned previously, it’s always a good idea to reuse NSDateFormatters whenever possible.
However, if you need more speed, you can parse dates directly using C instead of NSDateFormatter. Sam Soffes wrote a blog post about this topic which presents some code to parse ISO-8601 date strings. However, you can easily tweak his code examples to fit your particular needs.
Well, that sounds great — but would you believe there’s an even better way?
If you can control the format of the dates you are dealing with, choose Unix timestamps if at all possible. Unix timestamps are simply integers which represent how many seconds have passed since the “epoch”, which is simply the common reference date of 00:00:00 UTC on 1 January 1970.
You can easily transform this timestamp into an NSDate, as shown below:
- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp { return [NSDate dateWithTimeIntervalSince1970:timestamp]; } |
This is even faster than the C function!
Note that many web APIs return timestamps as milliseconds, since it’s very common for Javascript to eventually consume and process this data. Just be aware that you’ll need to divide the timestamp by 1000 before passing it to the dateFromUnixTimestamp method.
Where to Go From Here?
The following WWDC videos are highly recommended viewing for anyone interested in optimizing the performance of their apps. You’ll first need to ensure that you’ve registered your Apple ID as a developer, but once that’s done, you can have your fill of any of the videos from WWDC 2012:
- #406: Adopting Automatic Reference Counting
- #238: iOS App Performance: Graphics and Animations
- #242: iOS App Performance: Memory
- #235: iOS App Performance: Responsiveness
- #409: Learning Instruments
- #706: Networking Best Practices
- #514: OpenGL ES Tools and Techniques
- #506: Optimizing 2D Graphics and Animation Performance
- #601: Optimizing Web Content in UIWebViews and Websites on iOS
- #225: Up and Running: Making a Great Impression with Every Launch
There’s also some videos from WWDC 2011 that contain some great information as well:
- #308: Blocks and Grand Central Dispatch in Practice
- #323: Introducing Automatic Reference Counting
- #312: iOS Performance and Power Optimization with Instruments
- #105: Polishing Your App: Tips and tricks to improve the responsiveness and performance
- #121: Understanding UIKit Rendering
There’s even more videos, mostly from iOS 5 Tech Talks:
- Your iOS App Performance Hitlist
- Optimizing App Performance with Instruments
- Understanding iOS View Compositing
Based on “Your iOS App Performance Hitlist” video there’s a post written by Ole Begemann, that is a textual summary about the original presentation by Michael Jurewitz.
Apple has also provided a very useful resource called Performance Tuning, which provides a lot of great additional tips and tricks about making your apps perform well on iOS.
I hope you found this collection of tips helpful. If you know of a good tip that isn’t mention here, or have any comments or questions, please join the forum discussion below!
相关推荐
You will gain a full understanding of the new Apple framework along with tips and tricks to interact with the game either by detecting taps, using sophisticated gesture recognizers, and moving ...
Database\Oracle Internals - Tips Tricks and Techniques for DBAs.mobi Database\OReilly High Performance MySQL.mobi Database\OReilly SQL and Relational Theory 2nd.mobi Database\OReilly SQL Cookbook.mobi...
Explore advanced topics and unconventional approaches to game development, including tips and tricks for creating unique gameplay experiences and enhancing the marketability of games on the App Store....
内容概要:该文档介绍了一个用C++编写的控制台应用程序,主要功能是在新年来临之际展示倒计时、播放音符以及渲染烟花效果,最终以艺术字体显示新年祝福语。具体实现了粒子系统来模拟烟花绽放,并定义了不同形状(如“2025”)由小点组成的图像,再逐帧更新显示,营造烟火燃放的视觉冲击力。此外还有通过 Beep 函数发出不同频率的声音以配合倒计时刻度,同时加入了输入姓名和许愿的功能增加互动感。 适用人群:熟悉C/C++语言基础的学生群体及开发者。 使用场景及目标:适用于希望通过生动有趣的小项目加深对控制台操作的理解的学习者;也可以作为一个简单有趣的案例用于节日庆祝活动中。 其他说明:由于使用了许多特定于 Windows 平台的API函数,比如 Beep(), SetConsoleTextAttribute() 和 GetStdHandle(), 本程序仅能在 Windows 上运行良好。并且涉及到了较多关于粒子系统和声音处理的知识点,在教学过程中可以借此讲解一些图形渲染的基本原理和音频处理方法。
儿歌、手指谣、律动.docx
【文章链接:https://blog.csdn.net/2403_86849624/article/details/145739426?spm=1001.2014.3001.5502】基于 MSP430 微控制器的环境监测系统的设计与实现。该系统集成了温湿度、光照度、烟雾浓度以及 PM2.5 浓度等多参数的监测功能,具备数据显示、阈值设置和报警等功能。通过硬件电路与软件程序的协同工作,系统能够实时、准确地获取环境信息,并为用户提供直观的数据展示和有效的预警。文中深入探讨了系统的硬件选型、电路设计、软件编程思路及关键代码实现,经实际测试验证,该系统运行稳定、性能可靠,在环境监测领域具有一定的应用价值。关键词:MSP430;环境监测;传感器;数据处理 随着工业化进程的加速和人们生活水平的提高,环境质量对人类健康和社会发展的影响愈发显著。准确、实时地监测环境参数,对于预防环境污染、保障人体健康以及推动可持续发展至关重要。
基于COMSOL仿真的电磁超声压电接收技术在铝板裂纹检测中的应用研究,COMSOL模拟:电磁超声压电接收技术在铝板裂纹检测中的应用,comsol电磁超声压电接收EMAT 在1mm厚铝板中激励250kHz的电磁超声在200mm位置处设置一个深0.8mm的裂纹缺陷,左端面设为低反射边界。 在85mm位置处放置一个压电片接收信号,信号如图3所示,三个波分别为始波,裂纹反射波(S0模态)和右端面回波(S0)。 ,comsol;电磁超声;压电接收;EMAT;裂纹缺陷;信号接收;波;始波;S0模态;右端面回波,电磁超声检测技术:裂纹缺陷定位与信号分析
MATLAB环境中基于PSO算法的机器人路径规划系统:可视化界面下的障碍物自定义与终点规划,MATLAB实现PSO算法的机器人路径规划系统:支持自定义障碍物、起点终点的可视化界面操作,基于MATLAB的粒子群优化(PSO)算法的机器人路径规划,可视化界面,可自定义障碍物,起点和终点。 ,MATLAB; 粒子群优化(PSO)算法; 机器人路径规划; 可视化界面; 自定义障碍物; 起点和终点,MATLAB PSO算法机器人路径规划与可视化界面
五电平无刷直流电机BLDC矢量控制仿真模型研究:转速稳定、多电平可调参数的Matlab Simulink模型应用,五电平无刷直流电机BLDC矢量控制仿真模型研究:转速稳定、多电平可调参数的Matlab Simulink模型应用分析,五电平无刷直流电机BLDC矢量控制仿真模型,给定转速1000r min,运行良好; 三电平,两电平均可做,可调参数; matlab simulink模型 ,五电平BLDC矢量控制; 仿真模型; 1000r_min; 三电平控制; 可调参数; Matlab_Simulink模型。,五电平BLDC矢量控制仿真模型:三电平可调参数,1000r/min良好运行,Matlab Simulink实现
VSG预同步控制与电流双环控制的Matlab仿真模型研究及电力电子入门指南,基于Matlab的VSG预同步控制仿真模型:涵盖并网逆变器、VSG控制及电流双环管理等多模块研究参考文献详实电力电子仿真入门指南,VSG预同步控制matlab仿真模型 主要模块: 并网逆变器+VSG控制+预同步控制+电流电流双环控制 锁相环、三相准PR控制、PWM。 并附带参考文献,内容详实,适合电力电子入门仿真参考。 ,VSG预同步控制; MATLAB仿真模型; 并网逆变器; VSG控制; 预同步控制; 电流双环控制; 锁相环; 三相准PR控制; PWM; 参考文献。,Matlab仿真模型:VSG预同步控制及多模块协同仿真研究
WIFI密码查看器支持Windows系统,简单实用,欢迎下载
1998-2022年各地级市产业结构高级化数据(含原始数据+计算过程+结果) 1、时间:1998-2022年 2、指标:第二产业占比、第三产业占比、产业结构高级化 3、来源:城市统计NJ 4、计算说明:产业结构高级化=第三产业占比/第二产业占比 5、范围:290+地级市 6、缺失情况:缺失情况与年鉴一致,表内附有年鉴国内生产总值构成(三次产业占比)原始数据,以2022年地级市名单进行统计整理,2017年年鉴未统计全市层面数据,为市辖区数据
Skyline TerraExplorer Pro 5.1.3
1、文件内容:v4l-utils-0.9.5-4.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/v4l-utils-0.9.5-4.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、更多资源/技术支持:公众号禅静编程坊
项目工程资源经过严格测试运行并且功能上ok,可实现复现复刻,拿到资料包后可实现复现出一样的项目,本人系统开发经验充足(全栈全领域),有任何使用问题欢迎随时与我联系,我会抽时间努力为您解惑,提供帮助 【资源内容】:包含源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可实现复现;设计报告也可借鉴此项目;该资源内项目代码都经过测试运行;功能ok 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 【提供帮助】:有任何使用上的问题欢迎随时与我联系,抽时间努力解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 下载后请首先打开说明文件(如有);整理时不同项目所包含资源内容不同;项目工程可实现复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用
风力永磁同步发电机设计及仿真分析:Maxwell电磁仿真在1.5兆瓦风力发电机中的应用与工况研究,基于Maxwell电磁仿真的1.5兆瓦风力永磁同步发电机设计与工况分析,1.5兆瓦风力发电机 maxwell电机电磁仿真 风力永磁同步发电机设计,分析及工况分析 ,关键词:1.5兆瓦风力发电机; Maxwell电机电磁仿真; 永磁同步发电机设计; 永磁同步发电机分析; 工况分析。,Maxwell电机电磁仿真:风力永磁同步发电机设计与工况分析
XChart插件,用来做图表,但是图表不能做出很好看的样式
基于FVC2002数据集的MATLAB指纹识别系统研究与应用,基于MATLAB的FVC2002指纹数据集识别系统研究与应用,基于MATLAB的指纹识别系统 数据集为FVC2002指纹数据集 ,基于MATLAB; 指纹识别系统; FVC2002指纹数据集,基于MATLAB的FVC2002指纹识别系统
内容概要:本文由甲子光年智库发布,探讨了人工智能发展的拐点以及DeepSeek作为AI技术变革标志性产品的现状和前景。文中指出,人工智能的发展经历了从技术驱动到需求驱动的战略转变,正处于第三阶段向第四阶段过渡时期。DeepSeek通过对算力的极致优化,不仅展示了性价比极高的训练路径,还引领了算力效率的新拐点。2025年起发布的AI模型逐渐减少参数量,以支持更多场景下的高效部署,特别是移动终端和边缘计算领域。与此同时,以深度强化学习为核心的R1训练框架与非Transformer架构下的液态神经网络模型(LFM)共同开启了新一轮算法革新浪潮。 适用人群:对AI发展趋势感兴趣的行业从业者和技术爱好者。 使用场景及目标:用于了解AI技术的当前瓶颈与发展方向,把握算法创新、算力优化等方面的具体进展。适用于投资者研判行业趋势、技术人员跟进最新研究成果。 其他说明:文章详细阐述了DeepSeek的产品特点与市场反响,以及在全球AI治理框架下各国政府的态度变化。值得注意的是,DeepSeek的性价比优势促进了普惠AI的发展,使更多的中小企业和个人开发者获得了参与高水平AI项目的可能性。此外,文章还提及了美国政府内部关于AI政策的变化情况,揭示了中美两国在未来科技竞争格局下的潜在博弈态势。
基于栅格法的D*算法路径规划在Matlab中的实现与注释:自定义起点、终点与障碍物位置,基于D*算法和栅格法的Matlab路径规划:自定义地图与避障路径搜索,D*算法路径规划 Matlab路径规划 栅格法 可自行更改绘制栅格地图,自定义起始点目标点位置、未知障碍物位置 遭遇障碍物,重新搜索路径! matlab实现 详细注释 仅提供代码 ,D*算法;Matlab路径规划;栅格法;自定义起始点目标点;未知障碍物;重新搜索路径;matlab实现;详细注释,Matlab实现D*算法栅格路径规划:自定义障碍与详细注释版