-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Refactor initializers and pointer management in MGLSource and MGLStyleLayer hierarchies #8626
Conversation
@@ -47,6 +47,14 @@ - (instancetype)initWithIdentifier:(NSString *)identifier source:(MGLSource *)so | |||
return self; | |||
} | |||
|
|||
- (instancetype)initWithRawLayer:(mbgl::style::CircleLayer *)rawLayer source:(MGLSource *)source | |||
{ | |||
if (self = [super initWithIdentifier:@(rawLayer->getID().c_str()) source:source]) { |
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 MGLSource can always be obtained from the mbgl::style::Layer
, so we could move -[MGLStyle sourceFromMBGLSource:]
to +[MGLSource sourceWithRawSource:]
in MGLSource_Private.h (alongside the existing -[MGLSource initWithRawSource:]
). That would make -[MGLStyle layerFromMBGLLayer:]
a bit cleaner, since we wouldn’t need to pass in an MGLSource every time we wrap mbgl::style::Layer
.
Not a big deal, but possibly worth doing the next time we have to touch this code.
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.
Not sure I follow. Are you suggesting to remove the source
parameter from the various initWithRawLayer
s? How would the implementation of initWithRawLayer
obtain a mbgl::style::Source *
to pass to +[MGLSource sourceWithRawSource:]
?
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.
Ah, you’re right – I mistakenly thought the mbgl::style::Layer
has a method to get the mbgl::style::Source
, but of course it all comes from the mbgl::Map
by ID.
|
||
@interface MGLCircleStyleLayer (Private) | ||
|
||
- (instancetype)initWithRawLayer:(mbgl::style::CircleLayer *)rawLayer source:(MGLSource *)source; |
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.
Initially, I thought about declaring just -[MGLSource initWithRawLayer:source:]
(taking mbgl::style::Layer
in a private header and only declaring specialized methods like this in .m files. But this is safer, so 👍.
To elaborate, the call to We could fix this issue either by keeping a one-to-one relationship between Objective-C and C++ objects, as proposed in #7375 (comment) or #7375 (comment). In the meantime, a simpler fix would be to modify |
This doesn't seem to work. Presumably because I'll work on implementing the one-to-one relationship instead. |
5b5b30f
to
265b2ba
Compare
Updated the OP with the new, more complete, strategy here. @1ec5 Can you re-review, and in particular help me with the compiler warnings about "Designated initializer invoked a non-designated initializer"? When I try to add |
b8c9c12
to
ecc6f1d
Compare
The PR changed substantially since the review, so I’ll need to look more closely at it.
One solution would be to add a separate |
This doesn’t appear to be the case. If I add an MGLShapeSource to the style: MGLPointFeature *point = [[MGLPointFeature alloc] init];
MGLShapeSource *shapeSource = [[MGLShapeSource alloc] initWithIdentifier:@"foo" shape:point options:nil];
[style addSource:shapeSource]; then, once MGLSource *source = [self.mapView.style sourceWithIdentifier:@"foo"];
|
But that's exactly what this PR is trying to undo and avoid, because it requires that every concrete class implement an essentially identical Is there another solution? Why does this work fine for
Yes, this implementation only guarantees that any two simultaneously live references are identical. This is enough to fix the issue noted in #8626 (comment). If we need object identity even with no live references, we'll need a more complex scheme, where the the peer stores a weak pointer when pending but strong otherwise. This will also increase the baseline memory consumption. |
Solving this for MGLSource required eliminating all the initializers of intermediate classes (namely MGLTileSource). I guess we need to eliminate |
Introduce `-[MGLSource initWithPendingSource:]`, allowing the base class to track the owned `_pendingSource` pointer and implement `-addToMapView:` and `-removeFromMapView:` without any casts.
Similarly to the previous commit, introduce `-[MGLStyleLayer initWithPendingLayer:]`, allowing the base class to track the owned `_pendingSource` pointer and implement `-addToMapView:` and `-removeFromMapView:` without any casts. Fixes an issue where `-[MGLStyle layerFromMBGLLayer:]` would wind up creating layers whose `_rawLayer` and `_pendingLayer` held different values.
All `MGLSource` pointers referencing the same logical source will now be object identical; similarly for `MGLStyleLayer`.
It's already overridden by concrete subclasses, and by making it abstract we enable the removal of the private MGLForegroundStyleLayer category.
d1cf0b3
to
43bf2ff
Compare
I think I figured it out. The key seemed to be not the intermediate class initializers (the warning still occurred after 791654c), but rather providing |
This results in stack overflow for |
Unfortunately, there isn’t. (I guess anything is possible with swizzling, but there be dragons.) According to Objective-C’s initializer rules, each convenience initializer must call a designated initializer on So I think it was correct that |
MGLStyle has code that calls e.g. |
The root of the problem seems to be that designated initializers can't belong to a private category. That's really what we want: a set of initializers that are a) private except to concrete subclasses and b) must be called by concrete subclasses. If that was possible, we could find some way to satisfy the other Objective-C initializer rules (though they do seem unnecessarily restrictive to me). If that's not possible, I say we just remove |
…ubclasses It produces compiler warnings for which there seems to be no workaround, and since there's only a single initializer, NS_DESIGNATED_INITIALIZER has little benefit.
43bf2ff
to
e9bb3e1
Compare
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 for this fix. Removing the designated initializer attributes is unfortunate but unavoidable.
For the most part, there should be little developer impact, but let's add a note to both changelogs about the bug fix and the fact that these initializers have been moved to the concrete classes.
Introduce
-[MGLSource initWithPendingSource:]
, allowing the base class to track the owned_pendingSource
pointer and implement-addToMapView:
and-removeFromMapView:
without any casts.Similarly, introduce
-[MGLStyleLayer initWithPendingLayer:]
, allowing the base class to track the owned_pendingSource
pointer and implement-addToMapView:
and-removeFromMapView:
without any casts. This fixes #7437, where-[MGLStyle layerFromMBGLLayer:]
would wind up creating layers whose_rawLayer
and_pendingLayer
held different values.Finally, implement object identity for
MGLSource*
andMGLStyleLayer*
: allMGLSource
pointers referencing the same logical source will now be object identical; similarly forMGLStyleLayer
. This lays groundwork for #7375.MGLOpenGLStyleLayer