background

Not long ago, I made a rich text editing tool, but the editor encountered a performance problem of adding multiple pictures. When scrolling the editing area, there would be obvious lag phenomenon when switching pictures. This article analyzes the performance bottleneck and optimizes the performance bottleneck based on the performance problem.

You can open this link to the rich text editor implemented by UITableView on iOS to view my article, which is based on the project used for this article.

The results of

The result of the final analysis and optimization reduced the time from 90ms to 2ms, achieving a relatively smooth effect. For specific analysis and optimization steps, please see below.

Problem analysis

Since the problem occurs when the image is switched and placed in a separate Cell, try adding two logs to the Cell’s render method cellForRowAtIndexPath to see how long the method takes to execute.

Enter the image title here


2017-08-11 06:12:48.744 RichTextEditDemo[6867:1064632] ======begin Render Cell 2017-08-11 06:12:48.749 RichTextEditDemo[6867:1064632] ======end Render cell 2017-08-11 06:12:49.261 RichTextEditDemo[6867:1064632] ======begin RichTextEditDemo[6867:1064632] ======end render cell 2017-08-11 06:12:49.266 RichTextEditDemo[6867:1064632] ======end render cellCopy the code

Judging from the log printing time, it takes only a few milliseconds for each Cell to render, it seems that the problem will not appear in this location, however, this is not the truth, obviously, it will not be affected elsewhere, so use more advanced analysis tools to analyze and check.

Found the problem

Instrument is a good performance analysis tool to analyze memory allocation, memory leaks, network conditions, CPU usage, and other performance-related problems. The current performance problems are time-consuming problems. You can use Instrument’s Time Profiler to analyze the list. And there is a picture Cell switch

Image list

It can be seen that the Time Profiler has the following records. In the red box is the value of the Time spent on Cell switching. The increase of this Time is obviously higher than other values, so this is the place we need to locate.

Tips

  • Alt + Mouse Wheel -> Scale the timeline
  • Shift + Mouse Wheel -> Move the timeline
  • Hold down the mouse box and choose -> Select and Position the timeline
Time profilers screenshots

The first step is to select a range on the time axis and mark this range for analysis, so as to locate the problem accurately, as shown in Figure (1). The second step is to choose a certain function in the stack, generally choose to OC function calls, even at the bottom of the function call to CF layer is the C language implementation is bad is analyzed, so the choice here is [UIImage drawInRect: blendMode: alpha] this function analysis, As you can see, the function call says it takes 92ms, which is a long time, so this should be where the lag is.

Enter the image title here

The time taken by this function depends on the size of the image. Select another time peak range that occurs when switching between smaller images

Enter the image title here

This place takes a little less time, but it also reaches 25ms, which has a certain impact on performance.

To solve the problem

Conclusions can be drawn from the above analysis: [UIImage drawInRect: blendMode: alpha] function call is can lead to performance problems, Because of the way UITextView internal processing images is by calling [UIImage drawInRect: blendMode: alpha] function to draw pictures.

UITextView handles images internally

Since it is the internal processing of UITextView, the behavior of this function call cannot be changed by the application layer, but UIImage objects can be controlled by us, or we can change the display of images to achieve the purpose of optimization, so there are the following two solutions.

Plan 1

The first option is to compress the previewed image, then set it to NSTextAttachmen and display it in UITextView

textAttachment.image = self.image;
// ===> Is changed to
// scaletoSize is used to compress the original image. The Image object in textAttachment is compressed
textAttachment.image = [self.image scaletoSize:showImageWidth];
Copy the code

After this modification, the roll to load time of the large image is reduced to about 40ms

Optimization of Scheme 1


Scheme 2

The above scheme for image compression, the cost of time or because [UIImage drawInRect: blendMode: alpha] function calls, so have a better plan? The answer is yes, you can compress the image passed to the UITextView into a very small size (you don’t have to do this, pass an empty UIImageView object, the main reason for setting the image is that the image area needs an edit cursor), Then add a UIImageView to the image area corresponding to the UITextView, and set the original image in the UIImageView. This scheme will be much better than scheme 1. Several modifications of plan II:

  1. Set NSTextAttachment image to an empty UIImage object
        //....
        NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
        CGRect rect = CGRectZero;
        rect.size.width = showImageWidth;
        rect.size.height = showImageHeight;
        textAttachment.bounds = rect;
        textAttachment.image = [UIImage new];
        
        NSAttributedString *attachmentString = [NSAttributedString attributedStringWithAttachment:textAttachment];
        //....
Copy the code
  1. Cell Adds an ImageView to display the Image
        [self.imageContentView mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self).offset(_imageModel.imageFrame.origin.x);
            make.top.equalTo(self).offset(_imageModel.imageFrame.origin.y);
            make.height.equalTo(@(_imageModel.imageFrame.size.height));
            make.width.equalTo(@(_imageModel.imageFrame.size.width));
        }];
Copy the code

The following is the analysis diagram after optimization using scheme 2

Optimization Scheme 2



cellForRowAtIndexPath
UITextView setAttributedText:
[UIImage drawInRect:blendMode:alpha]
[UIImageView setImage:]

conclusion

Instrument is a great tool. You can use it to easily locate performance problems. Once the problems are found, it is easy to find solutions.