-
Notifications
You must be signed in to change notification settings - Fork 6k
Fix iOS embedder memory management and other analyzer warnings #29623
Conversation
| * The name of the callback. | ||
| */ | ||
| @property(retain) NSString* callbackName; | ||
| @property(copy) NSString* callbackName; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Foundation objects with mutable variants (NSMutableString) should be copy.
| options:0 | ||
| error:NULL]; | ||
| return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; | ||
| [networkConfigArray release]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
networkConfigArray was leaked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The style in projects I worked on prior to the advent of ARC was that unless there was a compelling reason not to--e.g., code that's called in a tight loop--it's far better to autorelease as part of the alloc/init statement, because then there's no chance of introducing a leak later via early return. It also made it much easier to review code for correctness, because any line that did an alloc/init but not an autorelease was immediately suspect, triggering a check for a corresponding release.
So tl;dr, I would make this part of line 276 instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In MRC projects I've worked on the style guide was to init/release within a scope so we didn't hit memory highwater marks waiting for the autorelease pool to drain. And autorelease on return statements.
This style avoids memory bloat, but your suggested way probably makes it easier to do the right thing with memory management. MRC documentation/examples have fallen off the internet and there's no style guide in play, and Apple never published one. I'd rather avoid crashes and leaks than theoretical memory bloat issues (though they weren't theoretical in other projects I've worked on). So let's go with your style suggestion.
| * Set by the initializer. | ||
| */ | ||
| @property(nonatomic) FlutterSendKeyEvent sendEvent; | ||
| @property(nonatomic, copy, readonly) FlutterSendKeyEvent sendEvent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
engine/shell/platform/darwin/ios/framework/Source/FlutterEmbedderKeyResponder.mm
Line 487 in b7459d5
| _sendEvent = [sendEvent copy]; |
| if (weak.get().enableObservatoryPublication) { | ||
| [[weak.get() delegate] publishServiceProtocolPort:url]; | ||
| } | ||
| [url release]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
url was leaked.
| FlutterSendKeyEvent sendEvent = | ||
| ^(const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* userData) { | ||
| [_engine.get() sendKeyEvent:event callback:callback userData:userData]; | ||
| [weakSelf.get()->_engine.get() sendKeyEvent:event callback:callback userData:userData]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This didn't show up in the linter, but this retain cycle was exposed when I wrote the testReleasesKeyboardManagerOnDealloc and the view controller wasn't being released.
| - (void)encodeRestorableStateWithCoder:(NSCoder*)coder { | ||
| NSData* restorationData = [[_engine.get() restorationPlugin] restorationData]; | ||
| [coder encodeDataObject:restorationData]; | ||
| [super encodeRestorableStateWithCoder:coder]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was an analyzer warning, encodeRestorableStateWithCoder needs to encode the super class's state.
| if (@available(iOS METAL_IOS_VERSION_BASELINE, *)) { | ||
| auto device = MTLCreateSystemDefaultDevice(); | ||
| ios_version_supports_metal = [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v3]; | ||
| [device release]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MTLCreateSystemDefaultDevice is NS_RETURNS_RETAINED
| [self.keyboardManager addPrimaryResponder:responder]; | ||
| [responder release]; | ||
| FlutterTextInputPlugin* textInputPlugin = self.engine.textInputPlugin; | ||
| if (textInputPlugin != nil) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.engine is nullable. When I tested it threw when adding nil to the secondary responder array.
stuartmorgan-g
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with nits
| options:0 | ||
| error:NULL]; | ||
| return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; | ||
| [networkConfigArray release]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The style in projects I worked on prior to the advent of ARC was that unless there was a compelling reason not to--e.g., code that's called in a tight loop--it's far better to autorelease as part of the alloc/init statement, because then there's no chance of introducing a leak later via early return. It also made it much easier to review code for correctness, because any line that did an alloc/init but not an autorelease was immediately suspect, triggering a check for a corresponding release.
So tl;dr, I would make this part of line 276 instead.
| * of the key down event. | ||
| */ | ||
| @property(nonatomic) NSMutableDictionary<NSNumber*, NSNumber*>* pressingRecords; | ||
| @property(nonatomic, retain, readonly) NSMutableDictionary<NSNumber*, NSNumber*>* pressingRecords; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have readonly private properties?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't write this and I don't want to start a stylistic war, but I think properties should always preferred over ivars (and the backing ivar only touched in init, dealloc, getters, and setters), even when private, because of KVO, KVC, to enforce readonly, to enforce copy when it's readwrite.
It’s best practice to use a property on an object any time you need to keep track of a value or another object.
Can we put a pin in this question for this PR? I only did the bare minimum to make the analyzer happy, plus some tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm aware of the property vs ivar debate; I actually don't have a strong opinion on that. I was just curious about the addition of readonly to a private proprety. It just seems odd to say that code within a class can modify the collection, but not replace it with a different collection.
But this wasn't intended to be a PR-blocking question.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm aware of the property vs ivar debate; I actually don't have a strong opinion on that.
Guess I'm always geared up for a religious war in these kinds of reviews 😉
Re: the readonly, you're right, that wasn't a static analyzer warning. I did it because the property wasn't being set other than in init, so it could be made readonly, which I did in the spirit of preferring stricter property attributes, readonly, immutable, nonnull, etc. But yeah, it's not API just private so it doesn't really matter (unless you're KVOing on the property and need to handle updating notifications in the property setter, which we aren't). I can change it back if you prefer.
| * The primary responders added by addPrimaryResponder. | ||
| */ | ||
| @property(nonatomic) NSMutableArray<id<FlutterKeyPrimaryResponder>>* primaryResponders; | ||
| @property(nonatomic, retain, readonly) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question here.
shell/platform/darwin/ios/framework/Source/FlutterViewController.mm
Outdated
Show resolved
Hide resolved
shell/platform/darwin/ios/framework/Source/FlutterViewController.mm
Outdated
Show resolved
Hide resolved
shell/platform/darwin/ios/framework/Source/FlutterViewController.mm
Outdated
Show resolved
Hide resolved
shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h
Outdated
Show resolved
Hide resolved
5bf262e to
777d088
Compare
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| // FLUTTER_NOLINT: https://github.com/flutter/flutter/issues/93360 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is new.
FlutterUmbrellaImport is used in the copy_and_verify_framework_module target, is not included in the engine framework source, and cannot be compiled on its own.
When clang-tidy is run on this file, it fails with a clang-diagnostic-error (compilation failure). Add a FLUTTER_NOLINT to this file with link to flutter/flutter#93360.
…er#29623) 有一些内存泄露,有些代码修改是在新代码,这个分支就不合了
Run the static analyzer on the iOS embedder. Fix the issues, mostly related to memory management.
Hopefully soon we can run clang-tidy in post-submit or the push hooks flutter/flutter#91857
To run clang-tidy for iOS:
Pre-launch Checklist
writing and running engine tests.
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.