Why do people always focus on memory in development? Because even though it’s ARC now, if we don’t handle the memory problem on the heap well, we’ll still have a memory leak, and if we keep leaking too much memory, we’ll eventually crash.

Detection scheme:

  • Check manually, implement dealloc method, leave the current class will call;

  • Tool detection, using Xcode tools for detection;

  • Automatic detection, automatic detection of memory leaks, and print out the corresponding information;

1. Manual detection

-(void)dealloc -(void)dealloc -(void)dealloc -(void)dealloc -(void)dealloc

- (void)dealloc {
    NSLog(@"%s", __func__);
}
Copy the code

2. Introduce tool testing

Static memory leak analysis method

The following four problems are mainly analyzed:

1. Logic error: access null pointer or uninitialized variable, etc.

2. Memory management errors, such as memory leaks;

3. Declaration error: never used variable;

4. Api call error: library and framework not included.

How do you do static analysis

In the menu bar, find Product and click Analyze

According to the results of the operation, one by one to specific analysis. This is all tool analysis, need to screen their own can be ignored

Dynamic memory leak analysis method

Static memory leak analysis cannot detect all memory leaks because some memory leaks occur at run time, when the user is doing something. Xcode Instruments is a testing and debugging tool that comes with Xcode. Instruments provides many functions, including the following:

  1. Time Profiler: CPU Profiler tools analyze the execution Time of code.
  2. Core Animation: GPU time required for off-screen rendering and layer mixing.
  3. Leaks: a tool used to detect memory Leaks.
  4. Energy Log: power consumption detection tool.
  5. Network: traffic detection tool.

Tool use

Here I’m just going to introduce the Leaks memory leak detection tool. Go to Xcode -> Product -> Profile and select Leaks

  • Select Leaks, and in the Leaks column select CallTree;
  • The Call Tree gives us an approximate location, so we need to narrow down and filter the data.
  • Click on the CallTree below to find these filters:
  1. Separate by Thread: Analyze by Thread to make it easier to catch problem threads that are eating resources.
  2. Invert Call Tree: Invert Call Tree. Displaying the deepest call level at the top makes it easier to find the most time-consuming operations.
  3. Hide System Libraries: Hides System library files. Filter out the various system calls and display only your own code calls.
  4. Flattern Recursion Merge multiple stacks produced by the same recursive function (because the recursive function calls itself) into one.

After the above check, you can see the corresponding specific code. At this time, if you select a time-consuming operation, double-click it to enter the corresponding code and display the detailed consumption time. That’s the problem.

Iii. Automatic detection is introduced

MLeaksFinder

MLeaksFinder is an open source iOS memory leak detection framework from the WeRea D team. It is non-invasive to code and is very simple to use. It just needs to be introduced into the project, and if there is a memory leak, an alert automatically pops up after 3 seconds to show the captured information. It only detects leaks on UIViewController and UIView objects in your application by default. Since these two types of objects are the most vulnerable to memory leaks in applications, it is possible to set extensions in your code to detect other types of object leaks

How MLeaksFinder works

In general, when a UIViewController is removed by pop or dismiss, its view and its subview and so on are also released very quickly, unless we make it singleton or have a strong reference to it. MLeaksFinder does this by looking at the basic case, after a UIViewController is popped or dismissed for 3 seconds, to see if its view and its subview etc still exist. If so, This means that there may be a memory leak, and the pop-up box alerts the user.

- (BOOL)willDealloc {
    __weak id weakSelf = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [weakSelf assertNotDealloc];
    });
    return YES;
}

- (void)assertNotDealloc {
     NSAssert(NO, @“”);
}

Copy the code

The characteristics of MLeaksFinder

  • Non-intrusive code
  • Whitelist mechanism
  • A leak stack can be built
  • Extensibility and other special handling

MLeaksFinder has helped us find the memory leak object, but we don’t know exactly which chain is causing the circular reference, and users have to look at the code themselves to check, which is a waste of time. Memory leaks are usually caused by circular references.

FBRetainCycleDetector introduction

FBRetainCycleDetector is FaceBook’s open source tool for detecting strong reference loops. This is enabled by default in the DEBUG environment, but you can also set RETAIN_CYCLE_DETECTOR_ENABLED to always be enabled. Using this tool, you can pass in any Objective-C object in your application’s memory and FBRetainCycleDetector will look for circular references in the strong reference tree with that object as the root node.

  #import <FBRetainCycleDetector/FBRetainCycleDetector.h>

   _handlerBlock = ^{
       NSLog(@"%@", self);
   };

   FBRetainCycleDetector *detector = [FBRetainCycleDetector new];
   [detector addCandidate:self];
   NSSet *retainCycles = [detector findRetainCycles];
   NSLog(@"%@", retainCycles);
    
Copy the code

The two tools work together to make it easy to troubleshoot memory problems. MLeaksFinder is used to find the leaking object, and FBRetainCycleDetector is used to detect whether the object has circular references. If so, it is easier to check and modify the code according to the chain of circular references found.

The specific use

When the MLeaksFinder library is introduced, the FBRetainCycleDetector library is also introduced by default, which works in the Debug environment by default

pod ‘MLeaksFinder’

However, the following error is reported after the pull is run

Cannot initialize a parameter of type 'id<NSCopying> _Nonnull' with an rvalue of type 'Class'
Copy the code

There are two ways to solve this problem:

  1. Change the error code to
layoutCache[(id<NSCopying>)currentClass] = ivars
Copy the code
  1. Add these configurations to your Podfile as an official temporary solution
Post_install the do * * * * | installer | # # Fix for XCode 12.5 find_and_replace("Pods/FBRetainCycleDetector/FBRetainCycleDetector/Layout/Classes/FBClassStrongLayout.mm", "layoutCache[currentClass] = ivars;" , "layoutCache[(id<NSCopying>)currentClass] = ivars;" ) end def find_and_replace(dir, findstr, replacestr) Dir[dir].each **do** |name| text = File.read(name) replace = text.gsub(findstr,replacestr) if text ! = replace puts "Fix: " + name File.open(name, "w") { |file| file.puts replace } STDOUT.flush end end Dir[dir + '/'].each(&method(:find_and_replace)) endCopy the code

This is what it looks like when it’s configured

Common memory leak scenarios:

Currently, the root cause of a memory leak in an ARC environment is a circular reference in the code that prevents some memory from being freed and ultimately prevents the dealloc() method from being called. The main reasons are as follows:

  1. Retain Cycle, Block strong reference
  2. NSTimer used incorrectly
  3. Memory leaks caused by third-party providing methods
  4. CoreFoundation memory, forget to release

Here’s a quick example to see if a memory leak can be detected:

  • Circular reference caused by improper block usage

When you leave the screen, the popup box will pop up after a second. Click Retain Cycle to find the circular reference, which is clearly caused by the use of blocks by testView in VC.

  • Delegate causes a circular reference

This is due to the strong modifier used when declaring the delegate property

Pay attention to

There are some cases where you click to find a circular reference, but you can’t find it, so you have to check in the class yourself

#define MEMORY_LEAKS_FINDER_RETAIN_CYCLE_ENABLED 1 #define MEMORY_LEAKS_FINDER_RETAIN_CYCLE_ENABLED 1Copy the code

Existing problems

  1. The popover in MLeaksFinder is still implemented by UIAlertView, which has been abandoned in iOS9.0 and needs to be replaced by UIAlertController.
  2. UITextField always detects exceptions, this can be added to the whitelist;

Add a whitelist and the class will not be tested after the whitelist is added. The header file needs to be imported

#import <MLeaksFinder/NSObject+MemoryLeak.h>

[NSObject addClassNamesToWhitelist:@[NSStringFromClass([SecondViewController class])]];

Copy the code

There are some libraries that can be set to add import, such as Debug environment import

pod 'MLeaksFinder', :configurations => ['Debug']
Copy the code

Overall, MLeaksFinder + FBRetainCycleDetector is a high quality library that offers the perfect combination of utility and convenience.

extension

As a tool for everyday automation, it is hoped that MLeaksFinder can provide a callback interface itself, so that in the event of a memory leak, it can be reported in the background, like buried data.

Facebook engineers have been automating memory leak detection for iOS for a long time, and have published a nice article explaining how it works, as well as open sourcing their three suites.

Three open source tools:

1. Mainly used to detect circular references – FBRetainCycleDetector

2. It is mainly used for fast detection of potential memory leak objects and provided to FBRetainCycleDetector for detection -FBAllocationTracker

3. Visual tool, embedded directly into the App, can directly view the memory usage in the App, and filter potential leakage objects – FBMemoryProfiler

  • Automation of Facebook: client monitoring -> reporting to server -> categorizing/filtering -> distributing to designated persons -> handling memory leaks;
  • What is not open source is how the server categorizes and filters the reported circular reference chain, but Facebook engineers have a strategy for that as well;

FBmemoryProfiler is an open source tool library of FB for analyzing iOS memory usage and detecting cyclic references. It can also be introduced into the project for detection.

Reference article:

IOS memory leak detection automation

Automatically detects memory leaks on iOS