-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[ios, macos] Use std::vector for MGLMultiPoint coordinate storage #6890
[ios, macos] Use std::vector for MGLMultiPoint coordinate storage #6890
Conversation
@boundsj, thanks for your PR! By analyzing the history of the files in this pull request, we identified @incanus, @1ec5 and @jfirebaugh to be potential reviewers. |
This won't actually work in practice without #6888 😬 |
@@ -24,40 +25,24 @@ - (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUI | |||
|
|||
if (self) | |||
{ | |||
[self setupWithCoordinates:coords count:count]; | |||
_count = count; |
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.
We don’t need to maintain a _count
ivar as parallel state. Instead, implement -count
as _coordinates.size()
.
@@ -24,40 +25,24 @@ - (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUI | |||
|
|||
if (self) | |||
{ | |||
[self setupWithCoordinates:coords count:count]; | |||
_count = count; | |||
_coordinates.reserve(_count); |
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.
You can populate _coordinates
in one go with some pointer arithmetic:
_coordinates = { coords, coords + count };
or:
_coordinates.assign(coords, coords + count);
- (CLLocationCoordinate2D)coordinate | ||
{ | ||
assert(_count > 0); | ||
|
||
return CLLocationCoordinate2DMake(_coordinates[0].latitude, _coordinates[0].longitude); | ||
CLLocationCoordinate2D coordinate = _coordinates.at(0); |
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.
Structs like CLLocationCoordinate2D
are passed by value, so there’s no need to explicitly copy the struct below. Just return this value (or _coordinates.front()
).
{ | ||
free(_coordinates); | ||
} | ||
|
||
- (CLLocationCoordinate2D)coordinate | ||
{ | ||
assert(_count > 0); |
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.
While you’re here, can you change this to an NSAssert()
so we get better diagnostics?
@@ -83,19 +73,14 @@ - (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range | |||
|
|||
for (NSUInteger i = range.location; i < range.location + range.length; i++) |
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.
Replace this whole for
loop with:
std::copy(_coordinates.begin() + range.location, _coordinates.begin() + NSMaxRange(range), coords);
(Note the use of begin()
instead of end()
in the second parameter.)
[NSException raise:NSRangeException format:@"Reuse of existing coordinates array %p not supported", coords]; | ||
} | ||
else if (range.length == 0) | ||
if (range.length == 0) |
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.
If the developer tries to replace no coordinates, this method should be a no-op instead of raising an exception. That may help to minimize the number of special cases the developer would need to implement.
@@ -24,40 +25,24 @@ - (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUI | |||
|
|||
if (self) |
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.
It’s pretty meaningless to have a multipoint of zero points. In fact, we assert as much below. So we should detect that case early, in here, asserting if count
is zero.
@@ -107,22 +92,29 @@ - (void)replaceCoordinatesInRange:(NSRange)range withCoordinates:(CLLocationCoor | |||
} | |||
|
|||
[self willChangeValueForKey:@"coordinates"]; | |||
if (NSMaxRange(range) <= _count) | |||
NSUInteger newCount = NSMaxRange(range); |
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 way this method was implemented in the first place makes it possible to replace a segment at the end of the multipoint while appending. This is an antipattern that tends to mask off-by-one errors in client code. Methods such as -[NSMutableData replaceBytesInRange:withBytes:]
raise an NSRangeException
if the range exceeds the bounds of the receiver. I think this class should do likewise.
That would allow us to simplify this entire method to:
std::copy(coords, coords + range.length, _coordinates.begin() + range.location);
Plus an exception if NSMaxRange(range) > _coordinates.size()
and the KVO guards. We’d need a separate -appendCoordinates:count:
:
_coordinates.insert(_coordinates.end(), coords, coords + count);
In the unlikely event that the developer really does need to replace the tail end while appending, they can do it in two steps. With #6583, we’ll introduce -replaceCoordinatesInRange:withCoordinates:count:
to make it doable in one step:
_coordinates.erase(_coordinates.begin() + range.location, _coordinates.begin() + NSMaxRange(range));
_coordinates.insert(_coordinates.begin() + range.location, coords, coords + count);
(This is the most naïve approach; there are more performant approaches.)
Even though MGLPointCollection is immutable, it would be great if we could migrate it over to |
- (void)appendCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count | ||
{ | ||
[self willChangeValueForKey:@"coordinates"]; | ||
_coordinates.insert(_coordinates.end(), *coords); |
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 appends only the first coordinate in the passed-in array. To append the entire array:
_coordinates.insert(_coordinates.end(), coords, coords + count);
{ | ||
CLLocationCoordinate2D *coords = self.coordinates; | ||
mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty(); | ||
for (NSUInteger i = 0; i < [self pointCount]; i++) |
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 will call -pointCount
on every iteration. Use C++ fast enumeration instead:
for (auto coordinate : _coordinates) {
…
}
887ebf5
to
ccc14b8
Compare
Fixes #6580
cc @incanus @1ec5