Skip to content

Commit 32f200c

Browse files
committed
iOS: Eliminate FlutterEngine dealloc notification
FlutterEngineGroup keeps an array of all live FlutterEngine instances it has created such that after the first engine, it can spawn further engines using the first. Previously we manually managed this array by adding engines to it upon creation, then having [FlutterEngine dealloc] emit a notification such that FlutterEngineGroup can listen for it, and remove instances from the array upon dealloc. Instead, we now use an NSPointerArray of of weak pointers such that pointers are automatically nilled out by ARC after the last strong reference is collected. This eliminates the need for the manual notification during dealloc. Unfortunately, NSPointerArray is "clever" and assumes that if the array has not been mutated to store a nil pointer since its last compact call, it must must contain a nil pointer and can thus skip compaction. When holding weak pointers under ARC, this is no longer the case and so we work around the issue by storing a nil pointer before calling compact. See: http://www.openradar.me/15396578 I'm not thrilled with the fact that we're replacing one sort of TODO with another, but the code is much simpler and avoids relying on a trip through the notification center, which seems objectively better. Issue: flutter/flutter#155943
1 parent 9080611 commit 32f200c

File tree

6 files changed

+15
-42
lines changed

6 files changed

+15
-42
lines changed

shell/platform/darwin/ios/framework/Source/FlutterEngine.mm

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ static void IOSPlatformThreadConfigSetter(const fml::Thread::ThreadConfig& confi
8484

8585
#pragma mark - Internal constants
8686

87-
NSString* const kFlutterEngineWillDealloc = @"FlutterEngineWillDealloc";
8887
NSString* const kFlutterKeyDataChannel = @"flutter/keydata";
8988
static constexpr int kNumProfilerSamplesPerSec = 5;
9089

@@ -282,10 +281,6 @@ - (void)dealloc {
282281
}
283282
}];
284283

285-
[[NSNotificationCenter defaultCenter] postNotificationName:kFlutterEngineWillDealloc
286-
object:self
287-
userInfo:nil];
288-
289284
// nil out weak references.
290285
// TODO(cbracken): https://github.com/flutter/flutter/issues/156222
291286
// Ensure that FlutterEngineRegistrar is using weak pointers, then eliminate this code.

shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ @implementation FlutterEngineGroupOptions
1212

1313
@interface FlutterEngineGroup ()
1414
@property(nonatomic, copy) NSString* name;
15-
@property(nonatomic, strong) NSMutableArray<NSValue*>* engines;
15+
@property(nonatomic, strong) NSPointerArray* engines;
1616
@property(nonatomic, copy) FlutterDartProject* project;
1717
@property(nonatomic, assign) NSUInteger enginesCreatedCount;
1818
@end
@@ -23,7 +23,7 @@ - (instancetype)initWithName:(NSString*)name project:(nullable FlutterDartProjec
2323
self = [super init];
2424
if (self) {
2525
_name = [name copy];
26-
_engines = [[NSMutableArray<NSValue*> alloc] init];
26+
_engines = [NSPointerArray weakObjectsPointerArray];
2727
_project = project;
2828
}
2929
return self;
@@ -51,27 +51,26 @@ - (FlutterEngine*)makeEngineWithOptions:(nullable FlutterEngineGroupOptions*)opt
5151
NSArray<NSString*>* entrypointArgs = options.entrypointArgs;
5252

5353
FlutterEngine* engine;
54+
// NSPointerArray is clever and assumes that unless a mutation operation has occurred on it that
55+
// has set one of its values to nil, nothing could have changed and it can skip compaction.
56+
// That's reasonable behaviour on a regular NSPointerArray but not for a weakObjectPointerArray.
57+
// As a workaround, we mutate it first. See: http://www.openradar.me/15396578
58+
[self.engines addPointer:nil];
59+
[self.engines compact];
5460
if (self.engines.count <= 0) {
5561
engine = [self makeEngine];
5662
[engine runWithEntrypoint:entrypoint
5763
libraryURI:libraryURI
5864
initialRoute:initialRoute
5965
entrypointArgs:entrypointArgs];
6066
} else {
61-
FlutterEngine* spawner = (FlutterEngine*)[self.engines[0] pointerValue];
67+
FlutterEngine* spawner = (__bridge FlutterEngine*)[self.engines pointerAtIndex:0];
6268
engine = [spawner spawnWithEntrypoint:entrypoint
6369
libraryURI:libraryURI
6470
initialRoute:initialRoute
6571
entrypointArgs:entrypointArgs];
6672
}
67-
// TODO(cbracken): https://github.com/flutter/flutter/issues/155943
68-
[self.engines addObject:[NSValue valueWithPointer:(__bridge void*)engine]];
69-
70-
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
71-
[center addObserver:self
72-
selector:@selector(onEngineWillBeDealloced:)
73-
name:kFlutterEngineWillDealloc
74-
object:engine];
73+
[self.engines addPointer:(__bridge void*)engine];
7574

7675
return engine;
7776
}
@@ -82,9 +81,4 @@ - (FlutterEngine*)makeEngine {
8281
return [[FlutterEngine alloc] initWithName:engineName project:self.project];
8382
}
8483

85-
- (void)onEngineWillBeDealloced:(NSNotification*)notification {
86-
// TODO(cbracken): https://github.com/flutter/flutter/issues/155943
87-
[self.engines removeObject:[NSValue valueWithPointer:(__bridge void*)notification.object]];
88-
}
89-
9084
@end

shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,16 @@ - (void)testSpawn {
3737
}
3838

3939
- (void)testDeleteLastEngine {
40+
__weak FlutterEngine* weakSpawner;
4041
FlutterEngineGroup* group = [[FlutterEngineGroup alloc] initWithName:@"foo" project:nil];
4142
@autoreleasepool {
4243
FlutterEngine* spawner = [group makeEngineWithEntrypoint:nil libraryURI:nil];
44+
weakSpawner = spawner;
4345
XCTAssertNotNil(spawner);
46+
XCTAssertNotNil(weakSpawner);
4447
}
48+
XCTAssertNil(weakSpawner);
49+
4550
FlutterEngine* spawnee = [group makeEngineWithEntrypoint:nil libraryURI:nil];
4651
XCTAssertNotNil(spawnee);
4752
}

shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -262,23 +262,6 @@ - (void)testSpawn {
262262
XCTAssertNotNil(spawn);
263263
}
264264

265-
- (void)testDeallocNotification {
266-
XCTestExpectation* deallocNotification = [self expectationWithDescription:@"deallocNotification"];
267-
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
268-
id<NSObject> observer;
269-
@autoreleasepool {
270-
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"];
271-
observer = [center addObserverForName:kFlutterEngineWillDealloc
272-
object:engine
273-
queue:[NSOperationQueue mainQueue]
274-
usingBlock:^(NSNotification* note) {
275-
[deallocNotification fulfill];
276-
}];
277-
}
278-
[self waitForExpectations:@[ deallocNotification ]];
279-
[center removeObserver:observer];
280-
}
281-
282265
- (void)testSetHandlerAfterRun {
283266
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"];
284267
XCTestExpectation* gotMessage = [self expectationWithDescription:@"gotMessage"];

shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@
2929

3030
NS_ASSUME_NONNULL_BEGIN
3131

32-
extern NSString* const kFlutterEngineWillDealloc;
33-
3432
@interface FlutterEngine () <FlutterViewEngineDelegate>
3533

3634
- (flutter::Shell&)shell;

shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
#import "flutter/shell/platform/darwin/ios/rendering_api_selection.h"
1212
#include "flutter/shell/platform/embedder/embedder.h"
1313

14-
extern NSString* const kFlutterEngineWillDealloc;
15-
1614
@class FlutterBinaryMessengerRelay;
1715

1816
namespace flutter {

0 commit comments

Comments
 (0)