Program Ape Evolution must-read: Make apps run faster and respond faster (IOS)

Source: Internet
Author: User
Tags gcd time zones uikit

Good text about app speed and response speed optimization, according to the individual understanding free translation, limited by the level and not rigorous, with the original address
PS, think I do better than dry translation of the brothers to the top of the code Oh!
The first part is to say the idea, too verbose, can jump directly to the second part.
The second part is a summary of some practical optimization techniques (the climax part).
The advent of the ipad has a huge impact on the quality of software in the industry. Apple has improved its access standards several times, most notably by requiring faster and smoother software. ipad can be quickly turned on and wake up, ipad app can be opened quickly, tap the home button app can quickly cut into the background. Desktop users tend to be more patient, but ipad users expect any action to be done instantaneously. Ironically, the ipad's computing power lags far behind desktop computers, but users don't care. There are only a handful of desktop app developers that have a good focus on performance optimization when developing ipad apps. Even with great concepts and good design, the experience is devastating when the app makes people feel slow.
Different apps, some respond quickly to the response delay, why there is such a difference? How do we make them more sensitive and fast? The entire article is divided into two parts, the first part we discuss the concept of app optimization, the latter part will refer to various optimization techniques. My goal is not to show all of the optimization techniques in detail, but to provide you with a guide to key technologies and why you should use them.


----------Part 1------------


Response speed vs. speed, which is more important?
There is a subtle difference between response speed and speed, which is the speed at which the user is listening for input to the feedback user, while the speed is the speed at which the task is processed.
Users hate to wait, so you'll say it's important to make the app run faster. This is true, but there is a boundary to the speed of the operation, and if the data is to be downloaded over the network, or for complex computations and renderings, then the app cannot immediately display the content. In this case the user is actually willing to wait, but you have to give an immediate response to their actions, which can be a simple button state change or a complex animation effect. It's important to make the app run faster, so it's just as important to respond quickly.
Most apps are like this, and our goal is for feedback to be executed before heavy computing.

<ignore_js_op>


Why is response speed so important?
Using real-life buttons and switches can make you feel good, and when you press a button or turn on the switch, you're sure you're done. But you can't feel it on the touchscreen, so the visual response is very important. If an app doesn't provide this instant response, the experience will become very bad, and more specifically, the response time should not exceed one-third seconds. When you click on a location but nothing happens, you will naturally think that clicking may not be accepted. Most people then click again, which can result in repetitive operations.
One of the great successes of the ipad is that most software makes people feel real. Apps often use a real-world approach to let users forget that they are using software (such as paper and ibooks), which is the way to use software easily and happily. But when apps often spend too much time responding to your touch, the software's good experience disappears. Most users can't tell whether an app has a good response, but they can tell which app is cool.
Three Principles
What do you do when your app feels a bit slow? Here are three simple principles to help you focus on the problem.
Immediate response
Quick feedback to users his actions have been accepted and then executed quickly. For example, when a button is clicked, a Touch-down status is presented to the user.
Allow users to interrupt at any time
When the time-consuming operation is performed, the user is fed a progress


-----------Part 2-----------


In this section I will talk about specific optimization techniques.
Operating speed
The previous section says that responsiveness is more important than the speed of operation, but we still need to start with speed optimization.
A theoretical approach (don't do It)
One of the things I learned from the computer Science course was the theory of finding ways to solve performance problems. Look at the following code:
A very large array with N elements
Nsarray * array = [self createarray];
For (ID object in array) {
[Object Performsomeaction];
}for (ID object in array) {
[Object Performanotheraction];
}
Compare the following
A very large array with N elements
Nsarray * array = [self createarray];
For (ID object in array) {
[Object Performsomeaction];
[Object Performanotheraction];}
In the first piece of code, your code will traverse the large array two times, which will be less efficient than completing all the tasks in a single traversal of the second piece of code. Paying attention to your code from this level will ensure that the algorithm is more efficient, but I don't think you need to do that. Modern compilers can intelligently optimize efficient final code, and the first piece of code deliberately written in the second piece of code is almost meaningless to the overall performance improvement. Sometimes this deliberate, code-level optimization can lead to code that is unclear and difficult to maintain (which the translator is very sympathetic to, writing elegant code rather than machine-friendly).
Measurement of performance
Apple provides us with powerful tools to measure app performance, so we don't have to rack our brains to assess how much time it takes to write out the code, and we can measure them directly.
The first rule of software optimization is to aim for improvements that can make a huge profit, and not to waste time on Code detail optimization. However, we can analyze from the point of view of the code which piece of improvement can bring greater benefits.
Choose Product from the Xcode menu, and then perform the profile. After a successful compilation, Xcode launches instruments, and later you can see the following popup box:

<ignore_js_op>


Here are a number of "instruments" that can help you analyze your app. The two (Time Profiler tools and core animation tools) above the red circle mark are most effective when the operating speed is focused.
Time Profiler
When we talk about running speed, we can't leave the time Profiler tool. The first thing to do when your program is slow is to investigate what takes up a lot of time. You can always find code blocks that can be written more efficiently, but before you rewrite the code, list the most time-consuming code blocks.
When you run the time Profiler, you get a list of method names. The following shows a list of methods that are sorted by time consumption, and you can add a starting and ending position in the timeline to focus on the different stages of your program.
Tick "Invert Call tree" and "Hide system Libraries" to filter the list, leaving only the methods you have written. If you do not tick "Invert call tree" you need to drill down into the innermost layer of the calling stack to see the time-consuming approach. You can try playing with these settings to get results that are useful to you.

<ignore_js_op>


Double-clicking a method name allows you to see exactly which line of code has taken so much time:

<ignore_js_op>


This is one of the most powerful features of time Profiler, and you can easily find out which code needs to be optimized.
In one of our projects, we used some nsdataformatter and nsnumberformatter to show the time and date of different formats and time zones. When I run time Profiler, it indicates that most of the time is consumed by the following code:
NSDateFormatter * df = [[NSDateFormatter alloc] init];
If you do not use time Profiler, it is difficult to notice this problem, we do not have to create nsdateformatter, so we rewrite this code:
NSDateFormatter * df = [self shareddateformatter];
Use the following method to prevent the creation of a new dateformatter each time:
Static NSDateFormatter * shareddateformatterinstance;-(nsdateformatter) Shareddateformatter {
if (shareddateformatterinstance = = nil)
Shareddateformatterinstance = [[NSDateFormatter alloc] init];
return shareddateformatterinstance;
}
With the simple rewrite above, we just called a nsdateformatter that time-consuming Init method, saving a lot of overhead.
Above is the experience of optimizing with time Profiler, and it is recommended that you consider time Profiler as part of your daily work process. Continuous optimization While writing code is much easier than going back to doing the so-called optimization task.
Stuttering/flickering
Unfortunately, time Profiler is not able to identify all the performance issues. When your app's frame rate falls below 60 (frames per second) Your app feels like it's not running as smoothly. Low frame rates cause scrolling views and animations to stutter.
A drop in frame rate usually means that the ipad renders at a slower speed. A visually ideal frame rate of not less than 60 (frames per second) means that each frame should be rendered within one-sixtieth seconds.
So this is the time for the core animation tool to debut. The Core animation can be used to measure the app's frame rate. There are some tick options on the left to help you record what's causing the low frame rate. We will revisit the more important items.

<ignore_js_op>


offscreen rendering (off-screen rendering)
The first concern is off-screen rendering. Off-screen rendering means that some areas of your app are rendered two times per frame. Most off-screen rendering is caused by shadows and masks. iOS first renders a shadow for the target layer and then renders the target layer, and the same mask also requires a process that first renders the target layer and then renders the mask for it.
When your app is forced to render off-screen rendering, the frame rate will be greatly compromised. Tick the color offscreen-rendered yellow to highlight all areas of off-screen rendering.

<ignore_js_op>


When off-screen rendering is caused by shadows, it is often easier to solve them. The time-consuming calculation of the shadow takes place on the exact shape of the calculated shadow, and the target layer will have to recursively traverse its child layers to calculate the shape of the shadow, so you can specify the shadow path for the target layer when you know it's shape. The shadow path determines the shape of the shadow.
We now assume this thumbview is rectangular. With the Bézier path we create nice round borders.
YourView.layer.shadowPath = [[Uibezierpath bezierPathWithRect:yourView.bounds] cgpath];
Now turn on the core Animation tool and rerun your app, and when those off-screen rendering is reduced or even wiped out, your app will become much smoother.
Blended layers (mixed layer)
Every time the ipad renders each frame, it calculates the color of each pixel. When there is an opaque layer at the top, it is very simple to calculate the final color of each pixel, just copy the corresponding point color of the topmost layer. The mixing layer (non-opaque layer) requires color blending of the corresponding screen area.
In a hierarchy of views, the more blending layers are, the greater the amount of rendering is calculated. If the topmost layer is a blending layer, the rendering engine needs to handle the layers covered below it, calculate the color of each point, and if the layer covered below is also a blending layer, the engine will continue to check the layer below it, so recursively.

<ignore_js_op>


You can check the number of blends by selecting "Blender layers". The dark red area indicates that the rendering of this area is laborious and may have multiple layers overlapping. If your app has too many red areas, you should consider adjusting the view hierarchy to flatter. You should also add a background color to a layer that does not require transparency at all and set its "Opaque" property to Yes, which is equivalent to telling the render engine that the layers below it do not need to be processed.
Rasterization (rasterization)
In some cases the lower layers are more difficult to render (using shadows, masks, complex shapes, gradients, etc.), to optimize the processing of this layer, iOS provides an API called "Rasterization" to cache it, which implicitly creates a bitmap that reduces the frequency of rendering.
[Layer Setshouldrasterize:yes];
The advantage of turning on rasterization is that the particular layer does not affect your overall frame rate at all, and the disadvantage is that rasterization will take up more memory while initializing will take up more time, and it will behave as pixelated (not vector graphics, but bitmaps) when scaling operations on that layer.
When you use Rasterization, you can turn on "Color Hits Green and Misses Red" to check if Rasterization is a good choice for this scenario. Rasterization can be of little use to optimizations if the rasterized layers become too red. Bitmap caches are removed from memory and re-created too frequently, and red indicates that the cache is rebuilt too late. You can selectively rasterize a smaller, deeper layer structure to try to reduce the rendering time.
Response speed
This is all about performance, and improving the performance of your app often leads to increased responsiveness, but not always. So I'm going to summarize some of the techniques that keep your interface responding quickly.
Threads
A thread can be considered a queue of instructions executed by the CPU in sequence. When method A calls method B, and Method B calls method C, all the code is executed sequentially. The benefits of this feature are obvious, and imagine how scary it would be if you were not sure what line of code was executed before you wrote the code. An app can have multiple threads that execute concurrently. The CPU continuously allocates execution time to each thread, and the threads do some work in the shorter time allotted.
Main thread
The main thread of the application is necessary to understand deeply. All user input and Uikit rendering is performed on the main thread. If you have not considered using multi-threading in your application, you can assume that your application is running on the main thread, and of course not quite so, iOS has done some optimizations in component encapsulation and will automatically drain some tasks into other threads.
Some applications only need to use the main thread is enough, all the code is executed linearly (strictly in the order you want), if the application experience is OK then you do not have to tangle too much on the thread problem. One important responsibility of the main thread is to respond to user input (tap, touch, gesture, etc.) because the thread can only do one thing at a time, and too much reliance on the main thread can cause you trouble (for example, to perform an operation that takes up to hundreds of milliseconds on the main thread). Your app needs to be able to respond quickly to user input while processing time-consuming calculations.
So the following code is very silly:
nsurlconnection * conn = [nsurlconnection sendsynchronousrequest:req returningresponse:res error:&error];
Or using a way that's not easy to see but still silly:
NSData * data = [[NSData alloc] initwithcontentsofurl:someexternalurl];
It is dangerous to do this in the main thread, and you have no idea how long your user interface will lose its response.
To sum up:
Most of the code executes on the main thread, including all the Uikit code and all event handling.
The main thread (as well as the other threads) can only do one thing at a time.
If your app performs a 3-second task on the main thread, it will lose its response for 3 seconds.
You can create and use a separate thread to perform time-consuming operations to avoid blocking the user interface.
Grand Central Dispatch
So how do you use threads? It is generally recommended to use GCD. GCD and threading are different, we don't delve into it here, but you'll find that GCD is a powerful set of mechanisms built on a thread. GCD is designed to run code in parallel (note not concurrency) and is especially powerful on multicore systems.
To use GCD, you first create a dispatch queue. You can do this:
Blocks added to this queue is executed serially
dispatch_queue_t backgroundqueue = dispatch_queue_create ("YourQueue", 0);
Or you can use a system of background queue, the system's background queue is configured to run in parallel with block. If you need block order execution, you can use Dispatch_queue_create to create a serial queue.
Blocks added to this queu can be executed concurrently
dispatch_queue_t existingqueue = dispatch_get_global_queue (dispatch_queue_priority_default, 0);
You can assign a task to a queue from another thread. The Dispatch_async will execute asynchronously, which means the method can return immediately without waiting for the block to execute. You can also assign a task to another queue when the queue executes the block.
Dispatch_async (Backgroundqueue, ^{
Your code that should run in the background
Do some heavy work here .....
Dispatch_sync (Dispatch_get_main_queue (), ^{
Notify the main thread here
});
});
Allow users to interrupt at any time
Although the thread is very powerful, it does not solve all of your problems:
Uikit is non-thread safe. This means that all classes and methods that have a UI prefix cannot be called in a background thread.
When a thread starts executing a piece of code, it's hard to stop (if you don't understand the phrase saying you're too young, the translator presses)
Multithreading makes your code complicated, and the thread sharing variable shared memory operation will haunt you.
The question above brings us to the last topic: Runloops. Runloops provides a mechanism for your app to still respond to input while the main thread executes the code. The primary runloop is a continuous loop that runs on the main thread, which listens for user input, updates the screen, and performs scheduled tasks (such as timers) during each loop. The following figure from the Apple official explains what to do in each loop, and your app stops responding because the Runloop is blocked by time-consuming code snippets. If you can break this time-consuming code snippet into lighter paragraphs and spread them across multiple loops, the problem will be solved because the app will listen for user input before performing a lightweight paragraph in each loop.

<ignore_js_op>


To do this, you have to break up the large piece of code into a small unit that is separated into a separate running loop. There are many ways to do this:
-(void) Someheavytaskparta {
First part of heavy stuff
Now call the next piece of code in the next Runloop with Performselector:afterdelay:
[Self performselector: @selector (SOMEHEAVYTASKPARTB) Withobject:nil afterdelay:0.0];
}
-(void) SOMEHEAVYTASKPARTB {
Second part of heavy stuff
Now call the next piece of code in the next Runloop with Performselector:afterdelay:
[Self performselector: @selector (SOMEHEAVYTASKPARTC) Withobject:nil afterdelay:0.0];
}
-(void) SOMEHEAVYTASKPARTC {
.......
But it's obviously not easy to write the code in this way, you can do it this way:
Because this code must run on the main thread we use the Mainqueue.
If your code can safely is executed on a background the thread you can
Also create your own operation queue that runs in the background like this:
Nsoperationqueue * YourQueue = [[Nsoperationqueue alloc] init];
[[Nsoperationqueue Mainqueue] addoperationwithblock:^{
First part of heavy stuff
}];
[[Nsoperationqueue Mainqueue] addoperationwithblock:^{
Second part of heavy stuff
}];
etc...
Cancel execution
If you allow users to interact with the app before a task is completed, you must consider the cancellation of the task. You can use a simple Boolean or reshape variable to cancel a block that has been scheduled to execute.
static int requestnumber = 0;
-(void) Cancel {
requestnumber++;
}
-(void) Schedulesomecode {
int currentrequestnumber = RequestNumber;
[[Nsoperationqueue Mainqueue] addoperationwithblock:^{
if (RequestNumber = = Currentrequestnumber) {
Part of heavy stuff that would not be executed if you have called Cancel
}
}];
}
All right, here we are, finally. We mentioned a lot about the optimization of the technology, I hope you can enjoy.

Program Ape Evolution must-read: Make apps run faster and respond faster (IOS)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.