Performance is especially important for iOS app development, and poor performance can mean things like your app being unresponsive or slow to respond.

There are three different levels: entry-level, intermediate and advanced:

  • Entry-level (Here are some tips you’ll definitely use in your app development)
    • 1. Use ARC to manage memory
    • 2. Use reuseIdentifier in the right place
    • 3. Make Views as opaque as possible
    • 4. Avoid large XIBs
    • Do not block the main thread
    • 6. Resize the Image in Image Views
    • 7. Select the right Collection
    • 8. Enable gzip compression
  • Intermediate (these are the ones you might use in relatively complex situations)
    • 9. Reuse and lazy loading Views
    • Cache, Cache, Cache!
    • 11. Weigh rendering methods
    • 12. Handle memory warnings
    • 13. Reuse expensive objects
    • 14. Use Sprite Sheets
    • 15. Avoid reprocessing data
    • 16. Select the correct data format
    • 17. Set Background Images correctly
    • 18. Reduce the use of Web features
    • 19. Set Shadow Path
    • 20. Optimize your Table View
    • 21. Select the correct data store options
  • Advancement (These tips should only be used if you are confident that they are problem-solvable and handy)
    • 22. Speed up startup time
    • 23. Use Autorelease Pool
    • 24. Select whether to cache images
    • 25. Try to avoid date format conversions

Beginner performance boost

This section focuses on some basic changes that can improve performance. But developers at all levels are likely to get a boost from this little performance memo documenting some neglected projects.

1. Use ARC to manage memory

ARC(Automatic Reference Counting) comes with iOS5, which avoids the most common memory leak that often happens when we forget to free memory. It automatically manages the retain and release process for you, so you don’t have to manually intervene. Here’s a code snippet you’ll often use to create a View:

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

Forgetting the release at the end of the code snippet is as easy as remembering to eat. And ARC will automatically do that for you at the bottom. In addition to helping you avoid memory leaks, ARC can also help you improve performance by ensuring that you free up memory for objects no longer needed. In this day and age, you should use ARC in all your projects!

Here are some resources to learn more about ARC: Apple’s Official Documentation Matthijs Hollemans’ 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!

ARC certainly can’t rule out all memory leaks for you. Memory leaks can still occur due to blocking, retain cycles, poorly managed CoreFoundation Objects (and C structures), or simply bad code. There is a great ARC can’t do and what should we do article conradstoll.com/blog/2013/1…

2. Use reuseIdentifier in the right place

In developing a common mistake is not to UITableViewCells UICollectionViewCells, even UITableViewHeaderFooterViews reuseIdentifier set up correctly. For performance optimization, the table view with a tableView: cellForRowAtIndexPath: for the distribution of rows cells, its data should reuse self UITableViewCell. A table view maintains a queue of data reusable UITableViewCell objects. Without the reuseIdentifier, a new cell would have to be set every time a table view is displayed. This can have a significant impact on performance, especially the scrolling experience of the app. As of iOS6, in addition to UICollectionView cells and supplementary views, you should also use reuseIdentifiers in header and footer views. To use reuseIdentifiers, add this method to your Data Source Object when adding a new cell to a table view:

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

This method removes existing cells from the queue or creates new cells using previously registered NIBs or classes if necessary. This method returns nil if there are no reusable cells and you haven’t registered a class or NIB.

3. Set the views as opaque as possible

If you have opaque Views, you should set their opaque property to YES. The reason is that this allows the system to render these views in an optimal way. This simple property can be set either in IB or in code. Apple’s documentation for setting opacity for images reads:Opaque this property gives the rendering system an indication of what to do with the view. If set to YES, the rendering system considers the view to be completely opaque, which allows the rendering system to optimize some of the rendering process and improve performance. If set to NO, the rendering system normally makes up the View with other content. The default value is YES. On relatively still screens, setting this property does not have much effect. However, if the view is embedded in the Scroll View or is part of a complex animation, not setting this property will greatly affect the performance of the APP. You can use the Debug\Color Blended Layers option in the emulator to find out which views are not set to Opaque. And the goal is, if you can make it opaque, make it opaque!

4. Avoid large XIBs

Storyboards added to iOS5 are rapidly replacing XIB. However, XIBs can still be useful in some scenarios. For example, if your app needs to adapt to pre-ios5 devices, or if you have a custom reusable view, you will inevitably use them. If you have to xiBS, keep them as simple as possible. Try to configure a separate XIB for each Controller, splitting the View hierarchy of a View Controller into a separate XIB as much as possible.Note that when you load a XIB everything is stored in memory, including any images. If you have a view that you don’t need right away, you’re wasting precious memory. Storyboards are another story, Storyboards only instantiate a View Controller when needed.

In XIB, all images are chache, as are sound files if you’re doing OS X development. When you load an NIB that references an image or sound resource, the NIB load code writes the image and sound file to memory. In OS X, image and sound resources are cached in named cache for future use. In iOS, only image resources are stored in named Caches. Depending on your platform, use the imageNamed: method of NSImage or UIImage to get the image resource.

Obviously the same thing happens in Storyboards, but I can’t find any documentation that supports this conclusion. If you know about this operation, write to me! To learn more about storyboards you can check out Matthijs Hollemans Beginning Storyboards in iOS 5 Part 1 and Part 2

Don’t block the main thread

Never overload the main thread. Because UIKit does all the work on the main thread, rendering, managing touch responses, responding to input, all that stuff needs to be done on it. The risk of using the main thread all the time is that if your code actually blocks the main thread, your app will become unresponsive. This is… The easiest way to get a star in the App Store :] The majority of cases that block the main process are when your App is doing I/O operations that involve reading or writing to external resources, such as storage or networking. You can use NSURLConnection to do network operations asynchronously: + (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler or use a framework like AFNetworking to do these operations asynchronously. If you need to do other types of costly operations (such as time-sensitive calculations or storage reads and writes) use Grand Central Dispatches, or NSOperation and NSOperationQueues.

The following code is a template for using GCD:

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
    });

});
Copy the code

Found a nested dispatch_async in your code? This is because any UIKit related code needs to be done on the main thread.

If you’re interested in NSOperation or GCD details, check out Ray Wenderlich’s Multithreading and Grand Central Dispatch on iOS for Beginners, And Soheil Azarpour’s How To Use NSOperations and NSOperationQueues tutorial.

6. Resize the Image in Image Views

If you want to display an image from a bundle in a UIImageView, you should make sure that the image is the same size as the UIImageView. Zooming in and out of images on the fly is expensive, especially if UIImageView is nested in a UIScrollView.

If the image is loaded from a remote service and you don’t have any control over the size of the image, for example, before downloading it, you can scale it once after downloading it, preferably with a Background thread, and then use the scaled image in the UIImageView.

7. Select the right Collection

Learning to choose the classes or objects that are most appropriate for your business scenario is the foundation for writing efficient code. This is especially true when dealing with collections.

Apple has a documentation for Collections Programming Topics that details the differences between available classes and which scenarios you should use them in. This is a must-read document for anyone who uses Collections.

Hehe, I knew you missed it for too long… Here’s a summary of some common collections:

  • Arrays: an ordered set of values. Index lookup is fast, value lookup is slow, and insert/delete is slow.
  • Dictionaries: Store key-value pairs. It’s faster to look it up by keys.
  • Sets: an unordered set of values. Quick to find by value, quick to insert/delete.

8. Enable gzip compression

Many apps rely on remote resources and third-party apis, and you may develop an app that requires downloading XML, JSON, HTML, or other formats from remote sources. The problem is that we’re targeting mobile devices, so you can’t expect the network to be good. A user may be on edge one minute and switch to 3G the next. Whatever the scenario, you don’t want to keep your users waiting too long. One way to reduce documentation is to open Gzip on the server and in your app. This is especially useful for text, which has a higher compression rate. The good news is that iOS already supports gzip compression by default in NSURLConnection, as do frameworks like AFNetworking based on it. Cloud service providers like Google App Engine already support compressed output. If you don’t know how to open Gzip using Apache or IIS, read this article.

Are you sure you’ve got the basics covered? But the reality is that sometimes some solutions aren’t as obvious as those, and they often depend heavily on how you structure and write your app. The following tips are for these scenarios.

9. Reuse and lazy load Views

More views means more rendering, which means more CPU and memory consumption, especially for apps that have a lot of views nested inside UIScrollView. The trick here is to mimic the actions of UITableView and UICollectionView: Instead of creating all subViews at once, create them as needed, and put them in a reusable queue when they’re done. This way you only need to create your views when scrolling occurs, avoiding uneconomical memory allocation. The energy efficiency issue of creating views also applies to other aspects of your app. Imagine a scenario where a user clicks a button and needs to present a view. There are two ways to do this:

  • 1. Create and hide the view when the screen is loaded and display it when needed;
  • 2. Create and present as needed.

Each option has its pros and cons: with the first option, because you need to create a view at the beginning and keep it until it is no longer used, it consumes more memory. However, it also makes your app more sensitive because when the user clicks on a button it just needs to change the visibility of the view. The second option is the opposite – consuming less memory, but running slightly slower when the button is clicked.

Cache, Cache, Cache!

A good rule of thumb is that the cache needs things that are not likely to change but need to be read frequently. What can we cache? Some options are the response from the remote server, images, and even computed results, such as the row height of a UITableView. By default, NSURLConnection caches the HTTP Headers loaded by the resource in memory or storage. You can even create an NSURLRequest manually and have it load only cached values. Here is a snippet of code that you can use to create an NSURLRequest for an image that will not change much and cache it:

    + (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;
    }
Copy the code

Note that you can get a URL request via NSURLConnection, as well as AFNetworking. This way you don’t have to change all your networking code to adopt this tip.

If you want to learn more about HTTP Caching, NSURLCache, and NSURLConnection, read this article.

If you need to cache something other than an HTTP Request, you can use NSCache. NSCache is similar to NSDictionary, except that when the system reclaims memory, it automatically deletes its contents. Mattt Thompson has a great article about it :nshipster.com/nscache/

If you are interested in HTTP, you can read Google’s Best-practices Document on HTTP Caching.

11. Weigh rendering methods

There are a lot of ways to make nice buttons in iOS. You can use whole images, resizable images, and uozhe can draw them using CALayer, CoreGraphics or even OpenGL. Of course, each solution has different levels of complexity and corresponding performance. A great post on graphic performance recommended by Andy Matuschak, a member of the Apple UIKit team, is well worth reading. Simply put, it’s faster to use pre-rendered images because it eliminates the need for iOS to create an image and draw something on it and then display it on the screen. The problem is that you need to put all the images you need into your app’s bundle, which increases the volume — and that’s where using variable-sized images is even better: you can save unnecessary space, and you don’t have to make different images for different elements (like buttons). Use images, however, also means that you lost the adjust the picture mobile use code, you need to constantly redo them again and again, and it is a waste of time, and if you want to do an animation effects, although each image is just some of the detail of the changes you need a lot of pictures of increasing the size of the bundle.

In general, you need to balance performance against keeping the bundle at the right size.

12. Handle memory warnings

IOS notifies all running apps when system memory runs low. The official documentation states that if your app receives a memory warning, it needs to free as much memory as possible. The best way to do this is to remove strong references for caches, image objects, and other objects that can be recreated. Fortunately, UIKit provides several ways to collect low memory warnings:

  • Use it in app DelegateapplicationDidReceiveMemoryWarning:The method of
  • Override in your custom UIViewController subclassdidReceiveMemoryWarning
  • Register and receive UIApplicationDidReceiveMemoryWarningNotification notification

Once you receive such notifications, you need to free up any unnecessary memory usage. For example, UIViewController’s default behavior is to remove invisible views, and some of its subclasses can supplement this method by removing additional data structures. An app with an image cache can remove images that are not displayed on the screen. Handling memory alerts in this way is essential, because if not, your app could be killed by the system. However, you must make sure that the object you select can be recreated to free up memory. Be sure to test out the memory reminder emulation in the emulator during development.

13. Reuse large overhead objects

Some objects are slow to initialize, such as NSDateFormatter and NSCalendar. However, you will inevitably need to use them, such as parsing data from JSON or XML. To avoid the bottleneck of using these objects, you need to reuse them, either by adding properties to your class or by creating static variables. Note that if you choose the second method, the object will remain in memory for as long as your app runs, much like a singleton.

The following code shows using an attribute to lazily load a date formatter. The first call creates a new instance, and subsequent calls 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;
    }
Copy the code

Also note that setting an NSDateFormatter is almost as slow as creating a new one! So if your app does a lot of date formatting, you’ll get a significant performance boost from this approach.

14. Use Sprite Sheets

If you’re a game developer, Sprite Sheets must be one of your best friends. Sprite Sheet makes rendering faster and even less memory efficient than standard screen rendering methods. We have two great tutorials for Sprite:

  • 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 the details of the Pixel format that can greatly affect the performance of your game.

If you’re not familiar with Spirte Sheet, check out these two (YouTube) SpriteSheets — The Movie, Part 1 and Part 2. The video was written by Andreas Low, author of Texture Packer, one of the popular tools for creating Sprite Sheet. In addition to using Sprite Sheets, the other tips written here can of course be applied to game development. For example, if you need a lot of Sprite Sheets, action essentials like enemies, missiles, etc., you can reuse these sprites without having to recreate them every time.

15. Avoid reprocessing data

Many applications need to load data, often in JSON or XML format, from the server for functionality. It is important to use the same data structure on both the server and client sides. Manipulating data in memory to fit your data structure is expensive.

For example, if you need data to display a table view, it’s best to get the array structure data directly from the server to avoid additional intermediate data structure changes. Similarly, if you need to fetch data from a particular key, a key-value pair dictionary is used.

16. Select the correct data format

There are many options for transferring data between apps and web services, the most common being JSON and XML. You need to choose the one that works best for your app.

Parsing JSON is faster than XML, and JSON is usually smaller and easier to transport. JSON deserialization is easy to use since iOS5 with official built-in JSON deserialization. But XML also has XML benefits. For example, parsing XML with SAX is like parsing local files; you don’t have to wait until the entire document has been downloaded to start parsing, as you do with JSON. When you deal with very large amounts of data, you can dramatically reduce memory consumption and increase performance.

17. Set the background image correctly

Putting a background image in a View is like many other iOS programming options: Use UIColor’s colorWithPatternImage to set the background color; Add a UIImageView to the view as a child view. If you are using a full-frame background, you must use UIImageView because UIColor’s colorWithPatternImage is used to create small repeating images as backgrounds. Using UIImageView in this case can save a lot of memory:

// You could also achieve the same result in Interface Builder
UIImageView *backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background"]];
[self.view addSubview:backgroundView];
Copy the code

If you use small tiling to create the background, you’ll need to do it with UIColor’s colorWithPatternImage, which renders faster and doesn’t cost much memory:

self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background"]];
Copy the code

18. Reduce the use of Web features

UIWebView is useful, it’s really easy to use it to display web content or create animations that UIKit can’t do. But you might have noticed that UIWebView isn’t as fast as driving Safari. This is due to limitations of Webkit’s Nitro Engine, which features JIT compilation. So for higher performance you need to tweak your HTML. The first thing to do is to remove unnecessary javascript whenever possible and avoid using overly large frameworks. It would be nice to just use native JS. Also, load javascript that doesn’t affect page presentation, such as user behavior statistics scripts, asynchronously whenever possible. Finally, always pay attention to the images you use and make sure they fit the size you use. Use Sprite Sheet for faster loading and memory savings. For more information, see WWDC 2012 Session #601 — Optimizing Web Content in UIWebViews and Websites on iOS

19. Set Shadow Path

How to add a shadow to a View or layer? The QuartzCore framework is the choice of many developers:

#import // Somewhere later ... UIView *view = [[UIView alloc] init]; // Setup the shadow ... View. The layer. ShadowOffset = CGSizeMake (1.0 1.0 f, f); The layer. ShadowRadius = 5.0 f; View. The layer. ShadowOpacity = 0.6;Copy the code

Seems pretty simple, right? However, the bad news is that using this method also has its problems… Core Animation has to draw and shadow your graphics in the background before rendering, which is expensive.

Using shadowPath avoids this problem:

view.layer.shadowPath = [[UIBezierPath bezierPathWithRect:view.bounds] CGPath];
Copy the code

With Shadow Path, iOS doesn’t have to calculate how to render every time, it uses a pre-calculated path. The problem is that calculating the path yourself can be difficult in some views, and you need to update the shadow path every time the View’s frame changes. Read more on this post by Mark Pospesel.

20. Optimize Table View

The Table view needs to have good scrolling performance, otherwise the user will find flaws in the animation during scrolling. To keep the table View scrolling smoothly, make sure you do the following:

  • The correct use ofreuseIdentifierTo reuse cells
  • Try to make all view Opaque, including the cell itself
  • Avoid gradients, zoom, and background selection
  • Cache line height
  • If the actual content in the cell comes from the Web, use asynchronous loading and cache the request results
  • useshadowPathTo draw a shadow
  • Reduce the number of subviews
  • Try not to applycellForRowAtIndexPath:If you need to use it, just use it once and cache the results
  • Use the right data structure to store data
  • userowHeight.sectionFooterHeightsectionHeaderHeightTo set a fixed height, do not request the delegate

21. Select the correct data store options

What do you do when storing large chunks of data?

You have many options, such as:

  • Use the * *NSUerDefaults**
  • Use XML, JSON, or PList
  • Archive using NSCoding
  • Use a local SQL database like SQLite
  • Using the Core Data,

What’s the problem with NSUserDefaults? While it’s nice and convenient, it only works with small data, such as simple Booleans, and beyond that you need to consider other options

What about structured files like XML? In general, you need to read the entire file into memory to parse, which is very uneconomical. Using SAX is another hassle.

NSCoding? Unfortunately, it also needs to read and write files, so it has the same problems.

In this scenario, SQLite or Core Data is preferable. With these techniques you can load only the objects you need with specific queries. SQLite and Core Data are very similar in terms of performance. They differ in how they are used. Core Data represents a Graph Model of an object, but SQLite is a DBMS. Apple recommends using Core Data in general, but if you have a reason not to use it, go for the lower-level SQLite. If you use SQLite, you can use FMDB(github.com/ccgus/fmdb)… The API.

## Advanced Performance Tips Want some elite tips on how to become a programming ape Ninja? Here are some tips to help you optimize your app to the hilt!

22. Speed up startup time

It’s important to open your app quickly, especially when you’re opening it for the first time. First impressions are so important to your app. What you can do is make it do as many asynchronous tasks as possible, such as loading remote or database data and parsing data.

Again, avoid xiBs that are too large because they are loaded on the main thread. Try to use Storyboards that don’t have this problem! Note that watchdog does not run with Xcode debug, be sure to disconnect the device from Xcode to test startup speed

23. Use Autorelease Pool

NSAutoreleasePool is responsible for releasing the Autoreleased objects in the block. Normally it’s automatically called by UIKit. But in some cases you need to create it manually. If you create a lot of temporary objects, you’ll find that memory will continue to decrease until the objects are released. This is because memory is only freed when UIKit runs out of autoRelease pools.

The good news is that you can avoid this behavior by creating temporary objects in your own @Autoreleasepool:

NSArray *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. */
    }

}
Copy the code

This code releases all autoRelease objects after each iteration refer to the official documentation for more information on NSAutoreleasePool.

24. Select whether to cache images

There are two common ways to load an image from a bundle, one is imageNamed, the other is image with content file, the first one is a little bit more common. Since there are two similar approaches to achieving the same goal, what is the difference between them?

The advantage of imageNamed is that it caches images when they are loaded. ImageNamed’s documentation says: This method looks up an image object in the system cache with a given name and returns it if it exists. If no corresponding image is found in the cache, this method loads the object from the specified document and then caches and returns the object. In contrast, imageWithContentsOfFile only loads images.

The following code illustrates the use of both methods:

UIImage *img = [UIImage imageNamed:@"myImage"]; 
// caching


// or

UIImage *img = [UIImage imageWithContentsOfFile:@"myImage"]; 
// no caching
Copy the code

So how should we choose? If you’re loading a large image and you’re using it once, there’s no need to cache the image, just use image with Content file so you don’t waste memory caching it. However, imageNamed is a much better choice in cases where images are reused repeatedly.

25. Avoid date format conversions

If you’re going to use NSDateFormatter to handle a lot of date formats, be careful. As mentioned earlier, it’s a good practice to reuse NSDateFormatters anytime.

However, if you need more speed, going straight to C is a good solution. Sam Soffes has a nice post (soff.es/how-to dras… Well, it looks good to do it in C, but can you believe we have a better solution?

If you can control the date format you work with, try to use Unix timestamps. You can easily convert from timestamp to NSDate:

- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp { 
    return [NSDate dateWithTimeIntervalSince1970:timestamp];

}
Copy the code

This will be faster than parsing a date string in C!

Note that many Web apis return timestamps in microseconds, because this format is easier to use in javascript. Remember to divide by 1000 before dateFromUnixTimestamp.

##### This article is from: 25 Tips and Tricks for iOS Application performance Tuning


More performance tuning articles:

IOS performance tuning —– Instruments iOS performance tuning, a must to be a qualified iOS programmer —– Instruments

⭐️ 25 tips and Tricks for iOS app performance tuning

Summary of iOS App Performance Optimization series

⭐️iOS tips for keeping the interface smooth

Zhihu: How does iOS achieve such good fluency?

(2017.11.21)

goyohol’s essay