Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 7c44aa8

Browse files
committed
[macos] FlutterKeyboardManager memory leak fix
Break cycle references, due to implicitely captured 'this'
1 parent cf12246 commit 7c44aa8

2 files changed

Lines changed: 43 additions & 4 deletions

File tree

shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,18 +123,24 @@ - (nonnull instancetype)initWithViewDelegate:(nonnull id<FlutterKeyboardViewDele
123123
[FlutterMethodChannel methodChannelWithName:@"flutter/keyboard"
124124
binaryMessenger:[_viewDelegate getBinaryMessenger]
125125
codec:[FlutterStandardMethodCodec sharedInstance]];
126+
126127
[keyboardChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
127128
[self handleKeyboardMethodCall:call result:result];
128129
}];
130+
129131
_primaryResponders = [[NSMutableArray alloc] init];
132+
133+
__weak __typeof__(self) weakSelf = self;
130134
[self addPrimaryResponder:[[FlutterEmbedderKeyResponder alloc]
131135
initWithSendEvent:^(const FlutterKeyEvent& event,
132136
FlutterKeyEventCallback callback,
133137
void* userData) {
134-
[_viewDelegate sendKeyEvent:event
135-
callback:callback
136-
userData:userData];
138+
__strong __typeof__(weakSelf) strongSelf = weakSelf;
139+
[strongSelf.viewDelegate sendKeyEvent:event
140+
callback:callback
141+
userData:userData];
137142
}]];
143+
138144
[self
139145
addPrimaryResponder:[[FlutterChannelKeyResponder alloc]
140146
initWithChannel:[FlutterBasicMessageChannel
@@ -143,14 +149,14 @@ - (nonnull instancetype)initWithViewDelegate:(nonnull id<FlutterKeyboardViewDele
143149
getBinaryMessenger]
144150
codec:[FlutterJSONMessageCodec
145151
sharedInstance]]]];
152+
146153
_pendingEvents = [[NSMutableArray alloc] init];
147154
_layoutMap = [NSMutableDictionary<NSNumber*, NSNumber*> dictionary];
148155
[self buildLayout];
149156
for (id<FlutterKeyPrimaryResponder> responder in _primaryResponders) {
150157
responder.layoutMap = _layoutMap;
151158
}
152159

153-
__weak __typeof__(self) weakSelf = self;
154160
[_viewDelegate subscribeToKeyboardLayoutChange:^() {
155161
[weakSelf buildLayout];
156162
}];

shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerTest.mm

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,9 +426,11 @@ - (bool)getPressedState;
426426
- (bool)keyboardChannelGetPressedState;
427427
- (bool)racingConditionBetweenKeyAndText;
428428
- (bool)correctLogicalKeyForLayouts;
429+
- (bool)shouldNotHoldStrongReferenceToViewDelegate;
429430
@end
430431

431432
namespace flutter::testing {
433+
432434
TEST(FlutterKeyboardManagerUnittests, SinglePrimaryResponder) {
433435
ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] singlePrimaryResponder]);
434436
}
@@ -461,6 +463,11 @@ - (bool)correctLogicalKeyForLayouts;
461463
ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] correctLogicalKeyForLayouts]);
462464
}
463465

466+
TEST(FlutterKeyboardManagerUnittests, ShouldNotHoldStrongReferenceToViewDelegate) {
467+
ASSERT_TRUE(
468+
[[FlutterKeyboardManagerUnittestsObjC alloc] shouldNotHoldStrongReferenceToViewDelegate]);
469+
}
470+
464471
} // namespace flutter::testing
465472

466473
@implementation FlutterKeyboardManagerUnittestsObjC
@@ -809,4 +816,30 @@ - (bool)correctLogicalKeyForLayouts {
809816
return TRUE;
810817
}
811818

819+
- (bool)shouldNotHoldStrongReferenceToViewDelegate {
820+
__strong FlutterKeyboardManager* strongKeyboardManager;
821+
__weak id weakViewDelegate;
822+
823+
@autoreleasepool {
824+
id binaryMessengerMock = OCMStrictProtocolMock(@protocol(FlutterBinaryMessenger));
825+
OCMStub([binaryMessengerMock setMessageHandlerOnChannel:[OCMArg any]
826+
binaryMessageHandler:[OCMArg any]]);
827+
828+
id viewDelegateMock = OCMStrictProtocolMock(@protocol(FlutterKeyboardViewDelegate));
829+
OCMStub([viewDelegateMock getBinaryMessenger]).andReturn(binaryMessengerMock);
830+
OCMStub([viewDelegateMock subscribeToKeyboardLayoutChange:[OCMArg any]]);
831+
832+
LayoutClue layoutClue;
833+
OCMStub([viewDelegateMock lookUpLayoutForKeyCode:0 shift:NO])
834+
.ignoringNonObjectArgs()
835+
.andReturn(layoutClue);
836+
FlutterKeyboardManager* keyboardManager =
837+
[[FlutterKeyboardManager alloc] initWithViewDelegate:viewDelegateMock];
838+
strongKeyboardManager = keyboardManager;
839+
weakViewDelegate = viewDelegateMock;
840+
}
841+
842+
return weakViewDelegate == nil;
843+
}
844+
812845
@end

0 commit comments

Comments
 (0)