Since Flutter has been plagued by memory leaks, many developers have been troubled by the problem. Since 0.9.4, the blogger started to fix the internal memory problems of flutter code at the engine level, which was solved. However, every version of Flutter needs to follow the official packaging, which is not very friendly to developers.

Thankfully, however, in later versions, a manual way to free memory was built in 😸


/**
 * Destroy running context for an engine.
 *
 * This method can be used to force the FlutterEngine object to release all resources.
 * After sending this message, the object will be in an unusable state until it is deallocated.
 * Accessing properties or sending messages to it will result in undefined behavior or runtime
 * errors.
 */
- (void)destroyContext;

Copy the code

Translation:

Destroy the engine’s running context. This method can be used to force the FlutterEngine object to release all resources. After this message is sent, the object will remain unavailable until unallocated. Accessing or sending a message to a property results in undefined behavior or runtime errors.

But, but, but (three important things to say) there is a lot of feedback about the Flutter Engine development group

  1. Unable to free memory completely

  2. I now collapse

  3. Official reply, carefully use github.com/flutter/flu…

What is the occasional collapse of ghost, temporarily did not encounter, difficult to say. The crash the blogger encountered before was due to the way he was using it. After FlutterVC was shut down, there were tasks executing methodChannel, that is, calling plugin, which could have been avoided in development. It is worth noting that flutter, implemented in c++, does not manage memory very well on its own

The self-test for memory problems is as follows

There is indeed a problem, there is still nearly 30M unreleased, take a look at the current memory object, as shown below

Let’s take a look at the ones that haven’t been released

android:LruCache

Least Recently Used The Least Recently Used algorithm. A page replacement algorithm for memory management. For unused chunks of data that are in memory (memory blocks), called LRU, the Flutter engine moves them out of memory based on which data belongs to the LRU to make room for loading additional data.

Dart ::BackgroundComplier class that optimizes the ISOLATE compilation

The GroundCompiler runs optimally compiled classes in background threads. Implementation: isolate one task per task, it disappears with having isolate, there is no OSR compilation in the background compiler.

dart::bin::socket

The mechanism by which the VM communicates with the development platform, such as jit just-in-time compiled DILL files, is passed to the DART VM through sockets, and the VM loads the file through RPC, resetting the thread, and implementing hotreload hot reloads

dart::BoolPrameter

  • dart::EnumParameter
  • dart::IdParameter
  • dart::IdParameter
  • dart::xxxPrameter

Dart VM, service.cc, derived from MethodParameter, parameter verification, parameter parsing. To compile the DART file

dart::OSThread

Responsible for operating system threads, thread creation, thread removal, thread lookup and management at DART runtime. The following figure

FlutterEngineRegistrar registers where the plugin is registered with the key. All plugin calls to the underlying DART methods are called back to the user via handleMethodCall, initialized where memory leaks are caused

- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine {
  self = [super init];
  NSAssert(self, @"Super init cannot be nil"); _pluginKey = pluginKey; // [pluginKey retain]; _flutterEngine = flutterEngine; // [flutterEngine retain];return self;
}
Copy the code

Here is an article that addresses circular reference articles for Engine

FlutterStandardMethodCodec codec standard method

FlutterStringCodec String codec FlutterJsonMessageCodec JSON codec

There are many ways to use jsonmssage in flutter, so you don’t need to initialize it every time

Where the code is implemented


@implementation FlutterJSONMessageCodec
+ (instancetype)sharedInstance {
  static id _sharedInstance = nil;
  if(! _sharedInstance) { _sharedInstance = [FlutterJSONMessageCodec new]; }return _sharedInstance;
}
Copy the code

STD :share_ptrxxx Shared pointer

Pointer to flutter ISOLATE Service DARTVM Symbolmapping

~~ article finished ~~

If you want to discuss flutter in depth, please join our QQ group 217429001

The complete test code is shown below

#import "FlutterTesterViewController.h"
#import <Flutter/Flutter.h>
#import "GeneratedPluginRegistrant.h"  
 
@interface FlutterTesterViewController ()
@property (nonatomic, weak) FlutterViewController * ctr;
@property (nonatomic, weak) FlutterEngine * engine;
@end

@implementation FlutterTesterViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    float Y = 210;
    [self createButton:@"Load boundle resource"Frame :CGRectMake(80.0, Y, 160.0, 40.0) Action :@selector(handleBoundleResource)]; Y += 40.0 + 10; [self createButton:@"autorelease"Frame :CGRectMake(80.0, Y, 160.0, 40.0) Action :@selector(handleAutoRelase)]; NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString * path = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"flutter_assets"]; NSLog(@"path: %@",path); } -(void)handleNetWorkResource:(UIButton *)button{} /** * load boundle resource */ -(void) handleBoundleResource { FlutterDartProject * dart = [[FlutterDartProject alloc] init]; FlutterEngine * engine = [[FlutterEngine alloc] initWithName:@"ios.dart.flutter"
                                                         project:dart];
    [engine runWithEntrypoint:nil];
    FlutterViewController* flutterViewController = [[FlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil];
    [GeneratedPluginRegistrant registerWithRegistry:flutterViewController];
    [self addBackButton:flutterViewController];
     [flutterViewController setInitialRoute:@"route1"];
    [self presentViewController:flutterViewController animated:YES completion:nil];
    self.engine = engine;
}

-(void)handleAutoRelase{
 
     FlutterBasicMessageChannel* channel;
    FlutterEngine * engine;
    @autoreleasepool {
        FlutterViewController* flutterViewController =
        [[FlutterViewController alloc] init];
        channel = flutterViewController.engine.systemChannel;
        engine = flutterViewController.engine;
        NSLog(@"engine111:%@",engine);
    }
    NSLog(@"engine222:%@",engine);
    [channel sendMessage:@"Hello!"];
    [channel setMessageHandler:^(id  _Nullable message, FlutterReply  _Nonnull callback) { }];
}

-(void)addBackButton:(UIViewController *)flutterViewController{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2  * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        UIButton * btn = [UIButton buttonWithType:UIButtonTypeSystem];
        [btn setTitle:@"Closed" forState:UIControlStateNormal];
        btn.frame = CGRectMake(10, 100, 50, 30);
        [btn addTarget:self action:@selector(buttonTap:) forControlEvents:UIControlEventTouchUpInside];
        [flutterViewController.view addSubview:btn];
        self.ctr = flutterViewController;
    });
}

-(void)buttonTap:(id)sender{
//    [self.navigationController popViewControllerAnimated:YES];
    
    __weak __typeof(self)weakSelf = self;
    [self.ctr dismissViewControllerAnimated:YES completion:^{
        
        [weakSelf.engine destroyContext];
    }];

}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}



-(UIButton *)createButton:(NSString *)title frame:(CGRect)frame action:(SEL)selector{
 
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button addTarget:self
               action:selector
     forControlEvents:UIControlEventTouchUpInside];
    [button setTitle:title forState:UIControlStateNormal];
    UIColor * bgcolor = [UIColor colorWithRed:arc4random()%256/255. green:arc4random()%256/255. blue:arc4random()%256/255. alpha:1];
    [button setBackgroundColor:bgcolor];
    button.frame = frame;
    [self.view addSubview:button];
    return button;
}

@end

Copy the code

Original is not easy, all rights reserved, reprint please notecode4flutter.com