-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Make source ownership consistent and make MGLGeoJSONSource's content properties mutable #6793
Conversation
@boundsj, thanks for your PR! By analyzing the history of the files in this pull request, we identified @1ec5, @frederoni and @ituaijagbone to be potential reviewers. |
Per #6254 (comment), MGLSource should follow the same invalidation pattern that we use in MGLOfflinePack, another class that wraps but does not own a C++ object. |
The distinction between |
#6254 (comment) seems like a more straightforward alternative to We could even avoid the invalidation pattern, if desired: if the developer calls |
Thanks @1ec5. Putting aside the lack of clarity about MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"mutable-data-source-features-id" features:@[smallBoxFeature] options:nil];
[self.mapView.style addSource:source];
MGLFillStyleLayer *layer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"mutable-data-layer-features-id" source:source];
MGLStyleValue *fillColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]];
layer.fillColor = fillColor;
[self.mapView.style addLayer:layer];
///
// sometime later...
///
source.features = @[largeBoxFeature]; In this scenario, a source is added to (and forever tied to as you've noted) the map. At any point, the features (or URL or data) of this I agree that this PR won't work for multiple map instances. I guess my question is would it be better to optimize for that up front with the |
My suggestion wouldn’t work for multiple map instances too – in fact, it makes the dependency more explicit, because the source would hold a weak pointer to an MGLMapView instead of some The rawSource/source approach is fine for now, but it should be documented so we understand the difference when we come back to this code later. My suggestion about making MGLSource a proxy for |
c33898c
to
5183994
Compare
5183994
to
d3a39ca
Compare
This is ready for review. It makes Notes:
|
Use common initialization logic to create an unique pointer to an mbgl source object, up front, when a MGL source is created. Keep a raw pointer to the unique pointer that is pointed at the mbgl source instance when a MGL source is created or when a MGL source is obtained by identifier from MGLStyle. Once the transfer of ownership of the mbgl source takes place, the unique ptr is null. The raw pointer can be used, internally, for future work that involves mutating the source.
URL example does not work as of this commit since a change in core still needs to happen to trigger an update.
If a GeoJSON source is initially set with features or data and then later on reset with a URL, features must be set to nil. This avoids a situation where features from a previously loaded GeoJSON source that no longer represent the current URL's features are still pointed to. When a URL is used to load the source, the features are unknown until the data is loaded (possibly over the wire) and since there is no public observer or callback API at the mbgl level, it is impossible to know when the source data is available to be converted to MGL features.
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.
Looks good to me.
@jfirebaugh, can you double-check that these changes match what you suggested in #6254 (comment)?
556201b
to
530f0a9
Compare
// no longer "pending". The move operation sets it to NULL. However, the | ||
// MGLSource's rawSource will still point to the mbgl source and clients | ||
// can mutate it via that reference. | ||
self.mapView.mbglMap->addSource(source.pendingSource); |
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.
Is it possible to put the std::move
here, rather than in the pendingSource
property accessor? That would make it clearer where transfer of ownership is actually occurring.
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.
Thanks @jfirebaugh. a43911e clarifies the transfer of ownership at the call site but might introduce other readability issues. I don't think we can do this with the @Property since it represents a single set/return signature for the value. I just did it this way (adding setter/getter) to make it work and to get a sense of how it would look. Maybe there is a better, alternative approach that allows the std::move
to stay at the actual ownership transfer location? cc @1ec5
If not, I'm tempted to revert a43911e unless you feel strongly about it.
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.
Here’s an idea that still requires indirection but limits the gymnastics to well-worn techniques:
- Remove the
_pendingSource
ivar from MGLSource and add it as an ivar in each concrete subclass instead. All the current calls to-setPendingSource:
become direct assignments to_pendingSource
. - Have each concrete subclass of MGLSource implement an
-addToMapView:
method that takes a reference to anmbgl::Map
and callsaddSource()
on it, passing instd::move(_pendingSource)
.
Do you anticipate any other reasons we’d need to get the pending source like that?
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 went with deleting a43911e @1ec5. IMO, the simplicity of working with the pendingSource
as a property outweighs the clarity benefit in the one place where it is actually transferred.
Relatedly:
Do you anticipate any other reasons we’d need to get the pending source like that?
I don't think there will be other reasons to get the pending source. The platform code should always deal with MGLSource
's public API (the content properties) and those use the raw pointer internally. The pending source pointer exists only to be transferred to the mbgl object.
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 pending source pointer exists only to be transferred to the mbgl object.
To me, that suggests we may want to go with the approach I outlined above. Not only does it make it clear where ownership is transferred, but it also prevents misuse of the pendingSource pointer. Currently you can use that property however you like.
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.
That's true. I'll go with your approach then.
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.
Fixed in 19dc2cb
The two issues noted above related to commits missing from |
@@ -28,6 +30,7 @@ - (instancetype)initWithIdentifier:(NSString *)identifier geoJSONData:(NSData *) | |||
{ | |||
_geoJSONData = data; | |||
_options = options; | |||
[self commonInit]; |
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.
-[MGLSource initWithIdentifier:]
should implement a private -commonInit
stub so that -[MGLSource initWithIdentifier:]
should call it and consolidate all these disparate calls. Each MGLSource subclass can still override the method to do things specific to the particular kind of mbgl::style::Source
it’s working with.
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.
That would be a nice refactor. However, commonInit
relies on having properties like URL
and geoJSONData
and features
set before it runs. If MGLSource
's initWithIdentifier:
calls commonInit
before the subclass init can set the requisite properties, then unexpected results will occur. I can imagine some ways to work around this but I think the disparate calls may actually be the best way to go, for now.
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.
Either revert a43911e or implement #6793 (comment).
a43911e
to
530f0a9
Compare
Is this setup such that a new ...Or is this a feature request? |
The There is overhead associated with either workflow, namely that the same features need to get round-tripped between C++ and Objective-C representations. We could alleviate that overhead by making the |
Hmm.... I'm getting
|
If you use the
Can you use |
Aha ok thanks for that explanation, it's working!
For the merging/mutating of the
|
WIP for source portion of: #6254
Fixes #6159