IOS performance optimization of memory (Memory) optimization

https://www.jianshu.com/p/8662b2efbb23

 

In recent work, the memory footprint of APP has been optimized to reduce the number of memory footprint, this will sum up the experience and share, we also welcome to communicate.

In the process of optimization, the main use of the following tools:

  • Instruments and Allocations
    This tool can show the actual memory footprint applications, and can be sorted by size. As long as we identify those with high occupancy, analyze the causes and find appropriate solutions.
  • MLeaksFinder
    Tencent open source tool to find a memory leak, you can use the APP process, instant reminder of a memory leak occurs.
  • Xcode's Memory Graph
    this tool to find memory leaks aspects can complement MLeaksFinder for circulation among the analyzed object reference relationship.
    Also by analyzing the Live Objects some point, you can analyze what is unreasonable.

To conclude, there are several reasons leading to high memory usage:

  • We had used unreasonable API
  • The image is too large to download network
  • Caching third-party libraries
  • Masonry Layout Framework
  • No need permanent memory objects, implemented as a permanent memory
  • Redundant fields in the data model
  • Memory Leak

The following discussion from several aspects.

1. unreasonable API

1.1 For use only once or very low frequency of use of the resources of the big picture, using [UIImage imageNamed:] loading method

Pictures loaded, there are two ways, one is [UIImage imageNamed:], the system cache loaded, and there is no API can be cleaned; the other is [UIImage imageWithContentsOfFile:]or [[UIImage alloc] initWithContentsOfFile:]the system will not cache processing, when the picture is no longer referenced, it occupies the memory will be completely released.

Based on the above characteristics, for use only once or very low frequency of use of the resources of the big picture, you should use the latter. When using the latter, pay attention to the picture can not be put in Assets.

1.2 Some image itself is very suitable for stretching mechanism of FIG. 9, but no corresponding optimization

Image memory footprint is large, suitable for stretching treatment mechanism of FIG. 9 pictures, can cut out a little more than the actual size of the image, thereby substantially reducing the memory footprint. For example, the following picture:



The solid part is left between the two vertical lines, the design of FIG when cut, for which a small portion cut out as long as it. Then we can use the slicing feature of Xcode, set the picture which is not part of the stretch, which parts of the stretching. When the picture is loaded, or loaded in the normal way.

1.3 In the absence of the necessary cases, the use of -[UIColor colorWithPatternImage:]this method

Projects have code uses UILabel, the background color of the label is set to a picture. In order to turn color pictures, using the above method. This method will refer to a loaded into memory in the picture, and then will create another image in memory, and memory footprint image is great.

Solution: In this scenario, it is reasonable to use UIButton, the picture is set to background. Although the use of UIButton generate two views will be more than UILabel, but from the image memory footprint compared, still totally worth it.

1.4 in the case where there is no need to use Core Graphics API, modifying a color of the object UIImage

Using this API, it will lead to additional generate an image in memory, large memory footprint. Reasonable approach is:

  • Set tintColor property of UIView
  • The picture is loaded in a way UIImageRenderingModeAlwaysTemplate
    code examples:
view.tintColor = theColor;
UIImage *image = [[UIImage imageNamed:name] imageWithRenderingMode: UIImageRenderingModeAlwaysTemplate] 

1.5 solid colors create a picture based on size is too large

Sometimes, we need to create a color-based UIImage, and used UIButton background images in different states. Because it is a solid color pictures, so we did not need to create and view the same image size, only need to create an image width and height are 1px size is enough.
Code Example:

//外部应该调用此方法,创建出1px宽高的小图像
+ (UIImage*)createImageWithColor:(UIColor *)color { return [self createImageWithColor: color andSize: CGSizeMake(1, 1)]; } + (UIImage*)createImageWithColor:(UIColor*)color andSize:(CGSize)size { CGRect rect=CGRectMake(0,0, size.width, size.height); UIGraphicsBeginImageContext(rect.size); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, [color CGColor]); CGContextFillRect(context, rect); UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return theImage; } 

1.6 Create a gradient image level, oversize

Some local projects based on color, use Core Graphics, create a horizontal direction from left to right gradient image in memory. Image size is the size of view, which in the view of some larger cases, caused no small memory overhead. @ 3x device on to a size of 400x60 view of an example, which is a memory overhead:
400. 3 * * *. 4. 3 * 60/1024 = 210KB.
But in fact on the image, if 400px width, height 1px, fully achieve the same effects, rather it is only the memory overhead:
400 * *. 1. 4/1024 = 1.56KB

From 1.7 UIView subclass defined by the drawRect: method to draw

Custom drawRect APP will consume a lot of memory, the larger view, consume more. Its memory consumption is calculated as:
memory consumption = (width * scale * height * scale * 4/1024/1024) MB

In almost all cases, drawing demand can be achieved through CAShapeLayer this weapon. CAShapeLayer on the CPU and memory footprint are two indicators after blasting drawRect :.
It has the following advantages:

  • Rendering fast. CAShapeLayer using hardware acceleration, the same graphics will be used to draw a lot faster than the Core Graphics.
  • Efficient use of memory. A CAShapeLayer do not like the ordinary boarding CALayer as to create a graphic, so no matter how big, will not take up too much memory.
  • It will not be clipped boundary layer.
  • Pixelation does not occur.

From 1.8 CALayer subclass defined by - (void) drawInContext: Method draw

And on a similar, try to use CAShapeLayer do draw.

1.9 UILabel oversized

If the size of a UILabel, greater than intrinsicContentSize, it will cause unnecessary memory consumption. So, when the layout view, we should try to make it equal to the size of UILabel intrinsicContentSize.
On this point, the reader can write a simple example program, then use the Instruments analysis tool, you can see Allocations in, Core Animation this one occupation will be significantly increased.

1.10 set the background color for the UILabel

If you set the background color is not clearColor, whiteColor, can cause memory overhead.
Therefore, once encountered this case, the structure can be converted into the view UIView+UILabel, the background color is set to the UIView, but is only used to display text UILabel.

It is also possible, using the Instruments tool to verify by writing a sample program.

2. Download the image is too large

Almost all iOS applications, will use this framework to load network SDWebImage picture. Sometimes encountered loaded picture is too large, in this case, need to be analyzed according to the specific scene, using different solutions.

2.1 view large images can not be scaled

If the big picture is reasonable, then we can only do is to download the pictures deleted from the cache memory when the view is released. Sample code is as follows:

- (void)dealloc { for (NSString *imageUrl in self.datas) { NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL: [NSURL URLWithString: imageUrl]]; [[SDImageCache sharedImageCache] removeImageForKey: key fromDisk: NO withCompletion: nil]; } } 

The code will allow a higher memory usage will only appear on a page, the page is returned once since then, the memory will return to normal.

2.2 Viewer small, then the picture should be scaled

If the view is used to display a small picture, and download a lot of pictures, then we should image to zoom process, and then save the picture scaled to SDWebImage memory cache.
Sample code is as follows:

//为UIImage添加如下分类方法:
- (UIImage*)aspectFillScaleToSize:(CGSize)newSize scale:(int)scale { if (CGSizeEqualToSize(self.size, newSize)) { return self; } CGRect scaledImageRect = CGRectZero; CGFloat aspectWidth = newSize.width / self.size.width; CGFloat aspectHeight = newSize.height / self.size.height; CGFloat aspectRatio = MAX(aspectWidth, aspectHeight); scaledImageRect.size.width = self.size.width * aspectRatio; scaledImageRect.size.height = self.size.height * aspectRatio; scaledImageRect.origin.x = (newSize.width - scaledImageRect.size.width) / 2.0f; scaledImageRect.origin.y = (newSize.height - scaledImageRect.size.height) / 2.0f; int finalScale = (0 == scale) ? [UIScreen mainScreen].scale : scale; UIGraphicsBeginImageContextWithOptions(newSize, NO, finalScale); [self drawInRect:scaledImageRect]; UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return scaledImage; } - (UIImage*)aspectFitScaleToSize:(CGSize)newSize scale:(int)scale { if (CGSizeEqualToSize(self.size, newSize)) { return self; } CGRect scaledImageRect = CGRectZero; CGFloat aspectWidth = newSize.width / self.size.width; CGFloat aspectHeight = newSize.height / self.size.height; CGFloat aspectRatio = MIN(aspectWidth, aspectHeight); scaledImageRect.size.width = self.size.width * aspectRatio; scaledImageRect.size.height = self.size.height * aspectRatio; scaledImageRect.origin.x = (newSize.width - scaledImageRect.size.width) / 2.0f; scaledImageRect.origin.y = (newSize.height - scaledImageRect.size.height) / 2.0f; int finalScale = (0 == scale) ? [UIScreen mainScreen].scale : scale; UIGraphicsBeginImageContextWithOptions(newSize, NO, finalScale); [self drawInRect:scaledImageRect]; UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return scaledImage; } //使用的地方 [self.leftImageView sd_setImageWithURL:[NSURL URLWithString:md.image] placeholderImage:[UIImage imageNamed:@"discover_position"] completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) { if (image) { UIImage *scaledImage = [image aspectFillScaleToSize: self.leftImageView.bounds.size scale: 2]; if (image != scaledImage) { self.leftImageView.image = scaledImage; [[SDWebImageManager sharedManager] saveImageToCache: scaledImage forURL: imageURL]; } } }]; 

3. Third-party libraries caching mechanism

3.1 Lottie animation framework

Information Lottie framework default cache animation frames, etc., if used in an application where a lot of animation, then with the accumulation of time, there will be a large number of cache information. However, after some cache information may no longer be used, and animated splash screen page cache result, for example.

For cache memory footprint caused Lottie, and according to their own wishes, choose between two approaches as follows:

  • Disable caching
[[LOTAnimationCache sharedCache] disableCaching]; 
  • Cache is not prohibited, but at the right time, to clear all cache, cache or an animation
//清除所有缓存,例如闪屏页在启动以后不会再次访问,那么可以清除此界面的动画所引起的缓存。
[[LOTAnimationCache sharedCache] clearCache]; //从一个页面返回后,可以删除此页面所用动画引起的缓存。 [[LOTAnimationCache sharedCache] removeAnimationForKey:key]; 

3.2 SDWebImage

SDWebImage caching mechanism is divided into two Disk and Memory, Memory This layer makes the picture when accessed without having to file IO processes and improve performance. By default, Memory is stored in the image data after decompression, this will lead to a huge memory overhead. If you want to optimize memory usage, you can choose to store the compressed image data, where the application startup add the following code:

[SDImageCache sharedImageCache].config.shouldDecompressImages = NO; [SDWebImageDownloader sharedDownloader].shouldDecompressImages = NO; 

3.3 YYModel

This library is very good, fast, easy to use. But everything has two sides, which in the internal cache of class information, and other types of content attribute information, and does not provide a public API to clear the cache. This can cause these caches will always exist, especially when a page is returned, which causes memory overhead can not be released.

So, if you want to optimize memory, it is recommended to remove the frame from the project instead of manually parsing. Although the time to write a little to spend more time, but on the CPU and memory performance, are the highest.

4.Masonry Layout Framework

This framework is almost every APP are introduced and heavily used, it is indeed very good, but there are some problems:

  • If you do not superView, or a parameter is nil, easily lead to a crash.
  • In the implementation process, it will create a lot of small objects larger than frame-based layout a lot of overhead.

So, my idea is that this framework can be used, but should reduce its use, especially in some pages will not be released, it should be no or little use, because it brings the memory overhead, it can not be released.

5. no need to subject permanent memory, implemented as a permanent memory

For like a sidebar, ActionSheet this interface objects, do not achieve permanent memory, should no longer be created to use the time that is spent for the destruction.

6. The redundancy fields in the data model

For the data returned from the server when parsing model, with the version of the iteration, there may be some fields are no longer used. If such a model will generate a lot of objects, then clean up for a redundant field in the model, you can also save a certain amount of memory footprint.

7. Memory Leak

Memory leak causes the application's memory usage has been increased, and not decreased. Pain points in practical work are: Fixed memory leak forefoot, rear foot another developer accidentally write in block in the self, or references to the instance variable, resulting in a memory leak occurs again.

Based on this, the introduction of the project ReactiveObjC two cattle X macros, @ weakify, @strongify, and the following wording specifications:

  • In block external use @weakify (self), you can define multiple weak references.
  • Use @strongify (self) at the beginning of the interior of the block, one can define a plurality of strong references.
  • Use self writing code inside the block
  • Non instance variables in block internal access class

Implement the above specification in teams, can effectively prevent the occurrence of circular references.



Author: buptwsg
link: https: //www.jianshu.com/p/8662b2efbb23
Source: Jane books
are copyrighted by the author. Commercial reprint please contact the author authorized, non-commercial reprint please indicate the source.

 

Guess you like

Origin www.cnblogs.com/sundaysgarden/p/12149034.html