X Tutup
Skip to content

Commit ba45595

Browse files
elijahrjcesarmobile
authored andcommitted
CB-14020: (ios) Fix "Collection was mutated while being enumerated" crash (#104)
* Avoid concurrent modification on callbacks list. Doesn't quite prevent it, but should be way better. * Actually synchronize things
1 parent 91c7313 commit ba45595

File tree

1 file changed

+48
-39
lines changed

1 file changed

+48
-39
lines changed

src/ios/CDVLocation.m

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ Licensed to the Apache Software Foundation (ASF) under one
66
to you under the Apache License, Version 2.0 (the
77
"License"); you may not use this file except in compliance
88
with the License. You may obtain a copy of the License at
9-
9+
1010
http://www.apache.org/licenses/LICENSE-2.0
11-
11+
1212
Unless required by applicable law or agreed to in writing,
1313
software distributed under the License is distributed on an
1414
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -65,7 +65,7 @@ - (void)pluginInitialize
6565
- (BOOL)isAuthorized
6666
{
6767
BOOL authorizationStatusClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(authorizationStatus)]; // iOS 4.2+
68-
68+
6969
if (authorizationStatusClassPropertyAvailable) {
7070
NSUInteger authStatus = [CLLocationManager authorizationStatus];
7171
#ifdef __IPHONE_8_0
@@ -75,7 +75,7 @@ - (BOOL)isAuthorized
7575
#endif
7676
return (authStatus == kCLAuthorizationStatusAuthorizedAlways) || (authStatus == kCLAuthorizationStatusNotDetermined);
7777
}
78-
78+
7979
// by default, assume YES (for iOS < 4.2)
8080
return YES;
8181
}
@@ -84,7 +84,7 @@ - (BOOL)isLocationServicesEnabled
8484
{
8585
BOOL locationServicesEnabledInstancePropertyAvailable = [self.locationManager respondsToSelector:@selector(locationServicesEnabled)]; // iOS 3.x
8686
BOOL locationServicesEnabledClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(locationServicesEnabled)]; // iOS 4.x
87-
87+
8888
if (locationServicesEnabledClassPropertyAvailable) { // iOS 4.x
8989
return [CLLocationManager locationServicesEnabled];
9090
} else {
@@ -112,10 +112,10 @@ - (void)startLocation:(BOOL)enableHighAccuracy
112112
}
113113
// PERMISSIONDENIED is only PositionError that makes sense when authorization denied
114114
[self returnLocationError:PERMISSIONDENIED withMessage:message];
115-
115+
116116
return;
117117
}
118-
118+
119119
#ifdef __IPHONE_8_0
120120
NSUInteger code = [CLLocationManager authorizationStatus];
121121
if (code == kCLAuthorizationStatusNotDetermined && ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)] || [self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)])) { //iOS8+
@@ -130,7 +130,7 @@ - (void)startLocation:(BOOL)enableHighAccuracy
130130
return;
131131
}
132132
#endif
133-
133+
134134
// Tell the location manager to start notifying us of location updates. We
135135
// first stop, and then start the updating to ensure we get at least one
136136
// update, even if our location did not change.
@@ -157,7 +157,7 @@ - (void)_stopLocation
157157
if (![self isLocationServicesEnabled]) {
158158
return;
159159
}
160-
160+
161161
[self.locationManager stopUpdatingLocation];
162162
__locationStarted = NO;
163163
__highAccuracyEnabled = NO;
@@ -169,14 +169,16 @@ - (void)locationManager:(CLLocationManager*)manager
169169
fromLocation:(CLLocation*)oldLocation
170170
{
171171
CDVLocationData* cData = self.locationData;
172-
172+
173173
cData.locationInfo = newLocation;
174-
if (self.locationData.locationCallbacks.count > 0) {
175-
for (NSString* callbackId in self.locationData.locationCallbacks) {
176-
[self returnLocationInfo:callbackId andKeepCallback:NO];
174+
@synchronized (self.locationData.locationCallbacks) {
175+
if (self.locationData.locationCallbacks.count > 0) {
176+
for (NSString* callbackId in self.locationData.locationCallbacks) {
177+
[self returnLocationInfo:callbackId andKeepCallback:NO];
178+
}
179+
180+
[self.locationData.locationCallbacks removeAllObjects];
177181
}
178-
179-
[self.locationData.locationCallbacks removeAllObjects];
180182
}
181183
if (self.locationData.watchCallbacks.count > 0) {
182184
for (NSString* timerId in self.locationData.watchCallbacks) {
@@ -193,7 +195,7 @@ - (void)getLocation:(CDVInvokedUrlCommand*)command
193195
[self.commandDelegate runInBackground:^{
194196
NSString* callbackId = command.callbackId;
195197
BOOL enableHighAccuracy = [[command argumentAtIndex:0] boolValue];
196-
198+
197199
if ([self isLocationServicesEnabled] == NO) {
198200
NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2];
199201
[posError setObject:[NSNumber numberWithInt:PERMISSIONDENIED] forKey:@"code"];
@@ -205,14 +207,18 @@ - (void)getLocation:(CDVInvokedUrlCommand*)command
205207
self.locationData = [[CDVLocationData alloc] init];
206208
}
207209
CDVLocationData* lData = self.locationData;
208-
if (!lData.locationCallbacks) {
209-
lData.locationCallbacks = [NSMutableArray arrayWithCapacity:1];
210+
@synchronized (self.locationData.locationCallbacks) {
211+
if (!lData.locationCallbacks) {
212+
lData.locationCallbacks = [NSMutableArray arrayWithCapacity:1];
213+
}
210214
}
211-
215+
212216
if (!__locationStarted || (__highAccuracyEnabled != enableHighAccuracy)) {
213217
// add the callbackId into the array so we can call back when get data
214-
if (callbackId != nil) {
215-
[lData.locationCallbacks addObject:callbackId];
218+
@synchronized (self.locationData.locationCallbacks) {
219+
if (callbackId != nil) {
220+
[lData.locationCallbacks addObject:callbackId];
221+
}
216222
}
217223
// Tell the location manager to start notifying us of heading updates
218224
[self startLocation:enableHighAccuracy];
@@ -228,19 +234,19 @@ - (void)addWatch:(CDVInvokedUrlCommand*)command
228234
NSString* callbackId = command.callbackId;
229235
NSString* timerId = [command argumentAtIndex:0];
230236
BOOL enableHighAccuracy = [[command argumentAtIndex:1] boolValue];
231-
237+
232238
if (!self.locationData) {
233239
self.locationData = [[CDVLocationData alloc] init];
234240
}
235241
CDVLocationData* lData = self.locationData;
236-
242+
237243
if (!lData.watchCallbacks) {
238244
lData.watchCallbacks = [NSMutableDictionary dictionaryWithCapacity:1];
239245
}
240-
246+
241247
// add the callbackId into the dictionary so we can call back whenever get data
242248
[lData.watchCallbacks setObject:callbackId forKey:timerId];
243-
249+
244250
if ([self isLocationServicesEnabled] == NO) {
245251
NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2];
246252
[posError setObject:[NSNumber numberWithInt:PERMISSIONDENIED] forKey:@"code"];
@@ -258,7 +264,7 @@ - (void)addWatch:(CDVInvokedUrlCommand*)command
258264
- (void)clearWatch:(CDVInvokedUrlCommand*)command
259265
{
260266
NSString* timerId = [command argumentAtIndex:0];
261-
267+
262268
if (self.locationData && self.locationData.watchCallbacks && [self.locationData.watchCallbacks objectForKey:timerId]) {
263269
[self.locationData.watchCallbacks removeObjectForKey:timerId];
264270
if([self.locationData.watchCallbacks count] == 0) {
@@ -276,7 +282,7 @@ - (void)returnLocationInfo:(NSString*)callbackId andKeepCallback:(BOOL)keepCallb
276282
{
277283
CDVPluginResult* result = nil;
278284
CDVLocationData* lData = self.locationData;
279-
285+
280286
if (lData && !lData.locationInfo) {
281287
// return error
282288
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageToErrorObject:POSITIONUNAVAILABLE];
@@ -292,7 +298,7 @@ - (void)returnLocationInfo:(NSString*)callbackId andKeepCallback:(BOOL)keepCallb
292298
[returnInfo setObject:[NSNumber numberWithDouble:lInfo.altitude] forKey:@"altitude"];
293299
[returnInfo setObject:[NSNumber numberWithDouble:lInfo.coordinate.latitude] forKey:@"latitude"];
294300
[returnInfo setObject:[NSNumber numberWithDouble:lInfo.coordinate.longitude] forKey:@"longitude"];
295-
301+
296302
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:returnInfo];
297303
[result setKeepCallbackAsBool:keepCallback];
298304
}
@@ -304,17 +310,20 @@ - (void)returnLocationInfo:(NSString*)callbackId andKeepCallback:(BOOL)keepCallb
304310
- (void)returnLocationError:(NSUInteger)errorCode withMessage:(NSString*)message
305311
{
306312
NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2];
307-
313+
308314
[posError setObject:[NSNumber numberWithUnsignedInteger:errorCode] forKey:@"code"];
309315
[posError setObject:message ? message:@"" forKey:@"message"];
310316
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:posError];
311-
312-
for (NSString* callbackId in self.locationData.locationCallbacks) {
313-
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
317+
318+
319+
@synchronized (self.locationData.locationCallbacks) {
320+
for (NSString* callbackId in self.locationData.locationCallbacks) {
321+
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
322+
}
323+
324+
[self.locationData.locationCallbacks removeAllObjects];
314325
}
315-
316-
[self.locationData.locationCallbacks removeAllObjects];
317-
326+
318327
for (NSString* callbackId in self.locationData.watchCallbacks) {
319328
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
320329
}
@@ -323,7 +332,7 @@ - (void)returnLocationError:(NSUInteger)errorCode withMessage:(NSString*)message
323332
- (void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)error
324333
{
325334
NSLog(@"locationManager::didFailWithError %@", [error localizedFailureReason]);
326-
335+
327336
CDVLocationData* lData = self.locationData;
328337
if (lData && __locationStarted) {
329338
// TODO: probably have to once over the various error codes and return one of:
@@ -336,10 +345,10 @@ - (void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)e
336345
}
337346
[self returnLocationError:positionError withMessage:[error localizedDescription]];
338347
}
339-
348+
340349
if (error.code != kCLErrorLocationUnknown) {
341-
[self.locationManager stopUpdatingLocation];
342-
__locationStarted = NO;
350+
[self.locationManager stopUpdatingLocation];
351+
__locationStarted = NO;
343352
}
344353
}
345354

0 commit comments

Comments
 (0)
X Tutup