В моем приложении я периодически записываю набор динамических данных в файл. Объект данных обновляется примерно каждую секунду. Иногда в одной из строк моего метода encodeWithCoder: метод я получаю исключение «Коллекция была видоизменена во время изменения». Каждый объект кодируется так:
[aCoder encodeObject:self.speeds forKey:@"speeds"];
Где self.speeds - это NSMutableArray. Я предполагаю, что проблема в том, что данные обновляются во время их кодирования. Я попытался использовать @synchronize в кодирующих блоках и , а также попытался сделать свойство атомарным, а не неатомарным, но ни один из них не работал. Сохранение происходит в фоновом режиме. Любые идеи о том, как сохранить эти данные в фоновом режиме, пока они обновляются? Мне хочется сделать копию, а затем сохранить ее, но не возникнет ли такая же проблема? Спасибо!
Редактировать 1:
Идея приложения заключается в том, что я открываю представление карты, которое периодически обновляет одноэлементный класс, содержащий массив объектов данных, причем каждый объект данных является информацией о карте пользователя. В каждом объекте данных, местоположениях пользователя, скоростях, высотах, расстоянии и т. Д. Каждые три раза менеджер местоположений обновляет местоположение пользователя, я обновляю текущий объект данных («живой» объект данных, который был только что создан для отслеживания этой поездки). в любой момент может быть только один «живой» объект данных) с новой информацией.
Я хотел бы записать весь синглтон в файл каждые x минут, но иногда запись и обновление происходят одновременно, и я получаю эту ошибку (или, по крайней мере, я так думаю) вызывает этот сбой). Проблема в моем коде или шаблоне дизайна?
Это метод кодирования в моем пользовательском классе:
- (void)encodeWithCoder:(NSCoder*)aCoder {
@synchronized([SingletonDataController sharedSingleton]) {
[aCoder encodeObject:[[lineLats copy] autorelease] forKey:@"lineLats"];
[aCoder encodeObject:[[lineLongs copy] autorelease] forKey:@"lineLongs"];
[aCoder encodeObject:[[horizontalAccuracies copy] autorelease] forKey:@"horAcc"];
[aCoder encodeObject:[[verticalAccuracies copy] autorelease] forKey:@"vertAcc"];
[aCoder encodeObject:[[speeds copy] autorelease] forKey:@"speeds"];
[aCoder encodeObject:[[overlayColors copy] autorelease] forKey:@"colors"];
[aCoder encodeObject:[[annotationLats copy] autorelease] forKey:@"annLats"];
[aCoder encodeObject:[[annotationLongs copy] autorelease] forKey:@"annLongs"];
[aCoder encodeObject:[[locationManagerStartDate copy] autorelease] forKey:@"startDate"];
[aCoder encodeObject:[[locationManagerStartDateString copy] autorelease] forKey:@"locStartDateString"];
[aCoder encodeObject:[[mapTitleString copy] autorelease] forKey:@"title"];
[aCoder encodeObject:[[shortDateStringBackupCopy copy] autorelease] forKey:@"backupString"];
[aCoder encodeFloat:pathDistance forKey:@"pathDistance"];
[aCoder encodeFloat:linearDistance forKey:@"linearDistance"];
[aCoder encodeFloat:altitudeChange forKey:@"altitudeChange"];
[aCoder encodeFloat:averageSpeedWithFilter forKey:@"avWithFilter"];
[aCoder encodeFloat:averageSpeedWithoutFilter forKey:@"avWithoutFilter"];
[aCoder encodeInt:totalTripTimeInSeconds forKey:@"totalTimeInSecs"];
}
}
Это метод обновления (в методе и других методах, вызываемых в методе обновления, есть больше кода, но я опускаю все, что не ссылается на «живой» объект dataObject
; тот, который обновляется) :
- (void)locationManager:(CLLocationManager*)manager didUpdateToLocation:(CLLocation*)newLocation fromLocation:(CLLocation*)oldLocation {
@synchronized([SingletonDataController sharedSingleton]) {
//create temporary location for last logged location
CLLocation* lastLocation;
if([dataObject.lineLats lastObject] && [dataObject.lineLongs lastObject]) {
lastLocation = [[CLLocation alloc] initWithLatitude:[[dataObject.lineLats lastObject] floatValue] longitude:[[dataObject.lineLongs lastObject] floatValue]];
} else {
lastLocation = [oldLocation retain];
}
//.....
//periodically add horizontal/vertical accuracy
if(iterations > 0 && iterations % 4 == 0) {
[dataObject.horizontalAccuracies addObject:[NSNumber numberWithFloat:[newLocation horizontalAccuracy]]];
[dataObject.verticalAccuracies addObject:[NSNumber numberWithFloat:[newLocation verticalAccuracy]]];
}
//.....
//accumulate some speed data
if(iterations % 2 == 0) {
NSNumber* speedNum = [[NSNumber alloc] initWithFloat:[newLocation speed]];
[dataObject.speeds addObject:speedNum];
[speedNum release];
}
//.....
//add latitude and longitude
NSNumber* lat = [[NSNumber alloc] initWithFloat:[newLocation coordinate].latitude];
NSNumber* lon = [[NSNumber alloc] initWithFloat:[newLocation coordinate].longitude];
if(fabs([lat floatValue]) > .0001 && fabs([lon floatValue]) > .0001) {
[dataObject.lineLats addObject:lat];
[dataObject.lineLongs addObject:lon];
}
if(iterations % 60 == 0) {
[[SingletonDataController sharedSingleton] synchronize];
}
}
}
И, наконец, метод synchronize
в классе SingletonDataController
(обновлен так, что теперь синхронизация происходит в асинхронном блоке согласно ответу Томми):
dispatch_async(self.backgroundQueue, ^{
@synchronized([SingletonDataController sharedSingleton]) {
NSLog(@"sync");
NSData* singletonData = [NSKeyedArchiver archivedDataWithRootObject:
[SingletonDataController sharedSingleton]];
if(!singletonData) {
return;
}
NSString* filePath = [SingletonDataController getDataFilePath];
[singletonData writeToFile:filePath atomically:YES];
}
});
где backgroundQueue создается следующим образом:
[sharedSingleton setBackgroundQueue:dispatch_queue_create("com.xxxx.xx", NULL)];
Я могу выложить больше кода, если это необходимо, но они, кажется, являются важными частями.