Memory Goblin DrawRect-talking about the memory optimization of drawing function

Source: Internet
Author: User

Body

The headline is a bit scary, but drawRect the evaluation is not too much. In the development of weekdays, the random coverage drawRect method, a little careless will make your program memory burst. Let's look at an example below.

Do an artboard function, but suffer from memory problems have not been solved. The artboard function is simple, which is to record the track of your finger touch and then draw it on the screen. Let's take a look at the following:

We see the state of the left memory deteriorating with the drawing of our fingers. Another careful friend can observe, click on the Blue Rectangle button in the picture, the artboard will pop up, and then no finger draw, the memory will be changed to the MB, and then whenever the finger drawing begins, the memory immediately increases to about three MB stable. For normal IOS apps, such a large memory consumption is intolerable.

Let's analyze Why:

There are two possible reasons why a large number of point objects created during finger drawing are not released in time or other resources are not released in a timely manner.
The second is that the system starts to consume a lot of memory during the drawing process.

The first reason is that a large number of point objects created during finger drawing are not released in time or other resources are not released in time. We temporarily exclude this to save time, because this artboard function works is ARC written, and we have done code checking and using Instruments tools to detect memory usage, there is no problem that the so-called object is not released in time.

For the second reason, the system starts to consume a lot of memory during the drawing process. First we noticed a weird and unusual thing is that when the yellow palette just popped up, the memory burst from 18MB to 114MB instantly. The first reason is not the problem, because at this point the finger has not yet made any drawing, that is, there is no point and line of the object, then how can memory explosion?

At this point we have to consider how this artboard function is implemented, the artboard is divided into two steps, the first step to record the user's finger trajectory, this step will generate a large number of points of the object (has been excluded suspects). The second step is to draw to the view or layer, we usually use the frequent drawing method is basically quarz2d's that set of C language framework, and where is the drawing code location? Our main character is finally on the pitch- drawRect .

Let's take a look at the code of the artboard feature drawing:

- (void)drawRect:(CGRect)rect{    if (!self.paths.count) return;    CGContextRef ctx = UIGraphicsGetCurrentContext();    for (BHBPaintPath *path in self.paths) {      CGContextSaveGState(ctx);      [[UIColor blackColor] set];      [path stroke]; // 关键的一步绘制      CGContextRestoreGState(ctx);    }}

To get rid of the plot context stack and the rest of the code that determines the bounds, we just view draw a black line on the current n one. How does it seem that the ordinary way of drawing can lead to a spike in memory? We now say that the culprit is drawRect not enough evidence. We recall the memory when the artboard just popped up, and then we commented out drawRect all the code. Run the following:

The effect is immediate, after comment out drawRect , memory immediately return to normal, we finally caught the consumption of memory of the devil, the problem is in the coverage of the drawRect method. So if we catch the prisoner, is this the end of the article? Not too, we know the reason for the memory explosion, but we don't have a deep analysis of drawRect why it's so big on memory, and we don't have a solution for the problem. Please look down.

So now let's analyze drawRect the real cause of the memory explosion:

Why does rewriting drawRect cause memory to rise a lot?

To figure this out, we need to go through the principles of the IOS program-shaped display. All views displayed in the IOS system are inherited from the base class and are UIView UIView responsible for receiving user interaction. but actually the view content you see, including graphics, is UIView drawn and rendered by an instance layer property, that is CALayer .

CALayerThe concept of a class is UIView very similar, it also has a tree-like hierarchical relationship, and can contain picture text, background color, and so on. It differs from the UIView biggest in that it cannot respond to user interaction, and it can be said that it does not know the existence of a response chain, and its API does not have the ability to respond, although it provides a "method for whether a point is in the layer range".

In each UIView instance, there is a default support layer that is UIView responsible for creating and managing this layer. In fact, this CALayer layer is really used to display on the screen, UIView just a layer of its packaging, implemented CALayer delegate , provides the specific functions of handling event interaction, as well as the animation of the underlying method of the advanced API.

Can be said to CALayer be UIView the internal implementation details.

How does it relate to today's theme drawRect ? Don't worry, since we have been sure CALayer is the final display on the screen, as long as the clues, can be analyzed clearly. In CALayer fact, it's just an ordinary class in IOS, and it doesn't render directly to the screen, because what you see on the screen is actually a picture. And why we can see CALayer the content, because there is CALayer a property inside contents . You contents can pass an object of one type by default, id but only when you pass CGImage it, it will be displayed on the screen normally. so eventually our graphic rendering landed on the contents body .

contentsAlso known as a homestay map, in addition to assigning it a value CGImage , we can directly draw it, the method of drawing is the key to this problem, through the inheritance UIView and implementation of the -drawRect: method can be customized to draw. The -drawRect: method does not have a default implementation, because for a reason UIView , a homestay map is not required and UIView does not care about what is drawn. If a UIView -drawRect: method is detected, it assigns a homestay map to the view, which is equal to the size of the view contentsScale (this property is related to the screen resolution, and our artboard program presents a different amount of memory in different emulators as well because of its value).

Then back to our artboard, when the artboard appears on the screen, because the method is overridden -drawRect: , the -drawRect : method is automatically called. After generating a homestay map , the code inside the method uses the Core Graphics line to draw n black lines, and then the content is cached, waiting -setNeedsDisplay to be updated the next time you call.

The method behind the artboard view -drawRect: is actually the bottom of the CALayer picture that has been redrawn and saved in the middle, CALayer the delegate property by default implements the CALayerDelegate protocol, when it needs content information will invoke the method in the protocol to take. When the artboard view is redrawn, because its support layer CALayer 's proxy is the artboard view itself, the support layer asks the artboard view to show it a homestay map, which is now called:

 - (void)displayLayer:(CALayer *)layer;

If the artboard view implements this method, you can get layer to set the contents homestay map directly, if this method is not implemented, the support layer CALayer will try to invoke:

 - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;

Before this method is called, CALayer an empty homestay map (size bounds and contentsScale decision) and a drawing context for a suitable size are created, Core Graphics prepared for drawing the homestay map, which is passed in as a ctx parameter. The empty homestay graph generated in this step is quite huge, it is the key to this memory problem, once you have implemented the method in the CALayerDelegate protocol -drawLayer:inContext: or UIView the -drawRect: method (in fact, the former packaging method), the layer creates a drawing context, The memory required for this context can be derived from this formula: 图层宽 * 图层高 * 4 字节 , the width of the height of the units are pixels. And our artboard program because to support the same as the ape question bank two finger move effect, we open the size of the artboard:

_myDrawer = [[BHBMyDrawer alloc] initWithFrame:     CGRectMake(0, 0, SCREEN_SIZE.width*5, SCREEN_SIZE.height*2)];

The artboard view of our artboard is that the amount of memory in iPhone6s plus the context of the machine is 1920*2 * *, which is the equivalent of 1080*5 4 字节 79MB Ram , which needs to be re-erased and redistributed each time the layer is redrawn. It is the real reason why our artboard program memory is exploding.

Finally we find out the reason for the memory explosion, so do we have a reasonable solution?

I think the most reasonable way to deal with the need to draw lines like Artboards is to use proprietary layers directly CAShapeLayer . Let's see what it is:

CAShapeLayeris a layer subclass that is drawn by vector graphics instead bitmap of drawing. Use CGPath to define the shapes you want to draw, which CAShapeLayer are rendered automatically. It can be a perfect substitute for our direct use of Core Graphics drawing layer , compared with the CAShapeLayer following advantages:

    • Rendering is fast. Cashapelayer uses hardware acceleration, and drawing the same graphic is much faster than using the Core graphics.

    • Efficient use of memory. A cashapelayer doesn't need to create a hosted graphic like a normal calayer, so no matter how big it is, it won't take up too much memory.

    • is not clipped by the layer boundary.

    • does not appear pixelated.

So in the end our artboard program uses CAShapeLayer to achieve the line drawing, the performance is very stable, as follows:

Summarize the Drawing performance optimization principles:

    • The best way to optimize the performance of graphics is not to draw.

    • Use proprietary layers instead of drawing requirements.

    • You have to use the drawing to minimize the view area and minimize the redraw frequency.

    • Asynchronously draws, guesses the content, draws the picture in advance on other threads, and sets the picture directly in the main thread.

Memory Goblin DrawRect-talking about the memory optimization of drawing function

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.