IOS development focuses on the FPS Optimization under Masonry, iosmasonry
The content of today's blog will systematically discuss the impact of Masonry on FSP and how to better use Masonry. If you are familiar with iOS development, you should be familiar with the Masonry framework. In short, the birth of Masonry makes the use of AutoLayout more elegant and makes the layout of controls more convenient. When we look at a thing from a dialectical point of view, everything has two sides, and Masonry's use is no exception. Improper use of the Masonry framework will directly affect the FPS of the UI. Today, we will discuss some misunderstandings about using Masonry and the usage methods that affect performance. This blog will still rely on the Demo to describe some things.
I have written an article dedicated to introducing the Masonry framework and parse the source code of the Framework. For more information, see the source code parsing of the Masonry framework for iOS development.
I. Demo Overview
1. Running Effect
The content of this blog is still based on the Demo we specially created for this blog. First, let's take a look at how the Demo works, through the Demo, we can see the problems and how they are solved. Below is the running effect of the Demo involved in this blog.
It is not difficult to see from the running effect below that we have divided six cases to observe and judge the impact of various Masonry usage methods on FPS. The preceding six segmentcontrols allow you to switch the Cell Layout mode. Of course, the Cell displayed in each layout mode is the same. This is also the principle of keeping the lab items consistent with other items during the experiment. You can use the FPS indicator in the lower right to intuitively view the trend of FPS changes. The FPS display control below is taken from our previous Demo. The previous Demo was about FPS optimization, but it was about FPS Optimization for Cell highly dynamic computing, for more information, see "Analysis of UI smoothness of various Cell highly adaptive implementation solutions developed by iOS".
The data displayed in the Cell below is randomly generated, and the Image on the left is also random. The Title and Detail on the right side are both NSAttributedString and some Detail at the bottom may be empty. If a Detail is empty, the layout of all content under the Detail is moved up. This Demo and its technical points will be detailed later.
2. Simulate network requests
The data displayed in the Cell above is obtained by simulating network data. below is the code for simulating the network layer. After all, it is a Demo, and the focus of the Demo is not on the network layer. I wrote it below, and the code is relatively simple. It is a singleton + a method for randomly generating simulated data, and then calls back the randomly generated data to the users at the network layer through Block. The Code is as follows:
3. The base class XBaseTableViewCell of the above Cell
The cells in each Segment above are an independent Cell type, but these cells have a common parent class. The parent class is responsible for processing the logic shared by these cells. The XBaseTableViewCell below is the base class of all cells displayed above. All controls on the Cell are declared and initialized. And provides related setting methods.
It is not difficult to see from the code below that there are two methods that need to be rewritten by subclass. One is to add the layout method addLayoutSubviews to the control, and the other is to update the layout method updateLayoutSubviews. Each subclass overwrites the two methods and provides different layout methods.
4. Code for switching Cell in Segment
Below is the logic code for clicking SegmentControl in the corresponding VC. Clicking different segments will select different cells and refresh the TableView. The code is relatively simple and will not be described too much.
Ii. Analysis of the above layout Methods
The next thing to do is to analyze the impact of each layout method on FSP. The following section analyzes the layout conditions using Instrument and looks at the specific data. The following describes the Update, Remake, Make, Update, and Frame operations that only use Masonry.
1. update
First, let's take a look at the update operation. That is, assign values directly to the control using update, which is a relatively lazy operation. In our Demo, the UI layout of some controls will be updated when cell values are set. For all the features, we simply use the update of Masonry to directly add constraints to the controls. The update operation in Masonry has a feature, that is, update a constraint will first find the constraint in the added constraint array, and then update the constraint, if not, add the corresponding constraint to install. From the perspective of this update function, the efficiency is relatively low.
I can take a look at the code implementation first, in the subclassXUpdateLayoutTableViewCell, RewrittenAddLayoutSubviewsAndUpdateLayoutSubviewsTwo methods. In the updateLayoutSubviews method, add constraints for all controls using the update method. In this case, the updateLayoutSubviews method is called every time you set the value. This will update all the la s of the controls on the cell. Of course, we do not recommend that you do this, this will update those constraints that do not need to be updated. The reason for listing it today is that the problem at the bottom of development does exist, maybe because of time shortage, or because of the lower implementation of the Code for other reasons.
Let's first use Instruments to run the Demo above, and then intuitively feel the intuitive performance of the Core Animation of the Demo. Below is the FPS data when we switch SegmentControl to Update. From the data below, we can easily see that using Update to add Update constraints affects FPS. Of course, attribute strings will be used in Cell, which will be discussed later.
We can run the Time Profile in the Update status. As shown below, it is not difficult to see from the following results that two operations are time consuming when Cell updates data. One is the update operation of Masonry, and the other is the operation of setting NSAttributedString with Label. Because each Label we use will assign an attribute string, which is a time-consuming operation. Another thing to be clear is that the creation and generation of attribute strings do not take much time, while the assignment and rendering of attribute strings take a lot of time, this is fromTime ProfileIs not hard to see.
2. remake
Next, let's take a look at the Remake operation. It is not difficult to see from the result of Core Animation below that the effect is not as good as the Update operation. The FPS below is lower than Update, which is also related to remake's own operations. remake literally means re-production. If you have already added constraints, remove them first, then add new constraints.
Below is the Time Profile corresponding to Remake. From the results, we can see that layout updates consume 66.6% of the Time, and uninstall consumes about 33% of the installation Time. In Masonry, remake is the most efficient. We will continue the discussion later.
3. make + update
After discussing update and remake, let's discuss the general practices of using Masonry. It is to use make to initialize the layout of the control and use update to update the constraints to be updated. Because the code is relatively simple, it won't be pasted up one by one, but it is necessary to run it with Instrument. Below is the result of Core Animation running in make + update mode. However, from the FSP results below, it is better than the previous use of only update or remake, but the FPS below is still not high, later, we will refine the data below.
This part of the Time Profile will not run, because we still use the Update constraints when setting the value, but not updating all constraints, but updating those constraints that only need to be updated. Because the number of updated constraints is smaller, the performance of all FPS will be better than that of all previously updated constraints.Make + updateThe FPS is slightly improved, but we can see from the figure below that the improvement is not particularly good.
4. frame + frame
Next, we will not use Masonry for layout. We will use Frame layout directly. Because Autolayout will eventually be converted to the Frame layout, it is obvious that the Frame layout is superior to the Autolayout layout in terms of performance. Next we will use the Frame layout and then use Frame update. The FPS below is not full. Most of the reason is because of NSAttrubitedString.
Let's take a look at how to update the Frame's Time Profile, as shown below. From the bottom, it is not difficult to see that only the time share of the update frame2.5%, Previously updated constraints can account60%Left and right, you can see the benefits of using the Frame layout. From the analysis results below, it is not difficult to see that the main factors affecting FPS have been transformed from the update layout to the AttributeString settings. This is also the reason why the above FPS is not full.
6. make + frame
The birth of Masonry is to facilitate the layout of controls. The Frame layout is not flexible enough, and it is complicated to adapt, so AutoLayout is available. HoweverAutoLayoutIt can easily adapt to the screen, but its performance is not particularly good. So can we combine them. That is to say, use make to initialize the layout of the control and use Frame to update the layout. Of course, this process is not easy to update the Frame when setting the value, because it is useless to update the Frame when the Cell sets the value, because after the Frame is updated, during rendering and display, it will still be displayed according to the layout of AutoLayout. What we need to do is to put the Frame layout after the Autolayout layout. here we need to put the code for updating the Frame into the next Runloop for execution. The code for updating the Frame is as follows:
In cell, make initializes the control layout, and uses Frame to update the layout, which is similar to Frame + Frame, except that when using Masonry layout, it is not as good as Frame layout when loading the first screen, later updates are the same. The following is the result of Core Animation in the form of Masonry + Frame. Although the effect is slightly worse than the previous one, the final effect is still full.
Iii. Summary
This blog only discusses the influence of Masonry's layout on FPS.NSAttributeStringDo not repeat the problem too much. If the presentation of rich texts affects FPS based on business needs, you can use other methods for optimization, such as displaying nodes provided by AsynDisplayKit. It is still necessary to make a summary at the end of the blog.
Below is the more detailed data in the Code. From the data, it is not difficult to see that Remake has the worst performance, so we try to use Masonry as much as possible.Less use of Remake. Updating the control is not a good option. If you want to use the Masonry framework and layout Update the control, it is best to separate those unchanged constraints from those that need to be updated. Use make to add related constraints, and use update to modify the constraints to be updated. Of course, Frame la s will provide better performance, but the layout process is too cumbersome to facilitate screen adaptation. Of course, you can also use Masonry to update the layout by Using Frame. Of course, you must note that the Frame layout must be updated after the Autolayout loading.
Below is a unified data statistics, of course, for the Demo corresponding to this blog. The table below calculates the average time of different methods used to update the cell Layout. From the data below, we can't see the rough Remake method, which consumes the most time to update the layout.12 + msAnd there are also a lot of Update constraints, and one Update layout is used.9 + ms. It takes only 7 + ms to update the layout, which is slightly better than updating all la S. Of course, it takes the least time to directly modify the Frame.0.06 + msFrom the data, you can intuitively feel the efficiency of the Frame layout.
The right side also shows the creation and assignment of an attribute string. We can see that the creation time of the attribute string is not too long, but the assignment of the attribute string is time-consuming, each assignment occupies0.7 ms,If there are 10, the value assignment time is7 msIf the content of the attribute string is more complex, it will definitely be higher than this. Of course, we can use some third-party controls and methods to optimize this part of time. This can be discussed later.
Today's blog is here for the purpose of reasonably using Masonry. If necessary, Frame layout can be used.
Demo-github share link: https://github.com/lizelu/FPSProfileDemo