在我的应用程序中,我定期将一组动态数据写入文件。数据对象大约每秒更新一次。有时,我的 encodeWithCoder: 方法中的一行出现“集合在被变异时发生变异”异常。每个对象的编码如下:
[aCoder encodeObject:self.speeds forKey:@"speeds"];
其中 self.speeds 是一个 NSMutableArray。我认为问题在于数据在编码时正在更新。我尝试在编码和保存块中使用@synchronize,并且我还尝试使属性原子而不是非原子,但都没有奏效。保存在后台进行。关于如何在更新时将这些数据保存在后台的任何想法?我觉得制作副本然后保存副本会起作用,但不会出现同样的问题吗?谢谢!
编辑1:
应用程序中的想法是我打开一个地图视图,它定期更新一个包含数据对象数组的单例类,每个数据对象都是用户的地图信息。在每个数据对象中,用户的位置、速度、高度、距离等。位置管理器每 3 次更新用户的位置,我就更新当前数据对象(刚刚创建用于跟踪这次旅行的“实时”数据对象——任何时候都只能有一个“实时”数据对象)带有新信息。
我想每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
(更新,以便现在根据 Tommy 的回答在异步块内发生同步):
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)];
如果需要,我可以发布更多代码,但这些似乎是重要的部分。