我有一个“广告”类,在其中我在 Ad.m 中设置了一个静态变量:
static NSDictionary *citiesDict = nil; // class variable
在同一个文件中,我实现了一个类方法,如果尚未加载,则基本上加载城市名称及其索引号的 plist 文件,最后将作为参数传递的数字值转换为城市名称:
+(NSString *) cityFromNumberValue:(NSString *)cityNumberValue
{
// load the cities from the plist file named "cities" if it's not already loaded
if (!citiesDict)
{
NSString *path = [[NSBundle mainBundle] pathForResource:@"cities" ofType:@"plist"];
citiesDict = [[NSDictionary alloc ]initWithContentsOfFile:path];
NSLog(@"loading plist");
}
NSLog(@"will return value");
NSArray *temp = [ citiesDict allKeysForObject:cityNumberValue];
NSString *key = [temp lastObject];
return key ;
}
并且总是在同一个文件中,我实现了一个 init 方法来将 Dictionary 转换为 Ad 对象,其中它使用 Class 方法 +cityFromNumberValue:cityNumberValue :
-(id) initWithDictionary: (NSDictionary *) dictionay
{
self = [super init];
if (self)
{
// convert the number to name of city
self.departureCity= [Ad cityFromNumberValue:[dictionay objectForKey:@"ad_villedepart"]];
self.arrivalCity= [Ad cityFromNumberValue:[dictionay objectForKey:@"ad_villearrivee"]];
}
return self;
}
而且我在同一个文件中还有一个从 Web 服务获取广告的方法,它在 for 循环中调用方法 +cityFromNumberValue:cityNumberValue: :
+(NSDictionary *) fetchAdOfPage : (NSInteger) page PerPage : (NSInteger) perPage
{
NSMutableArray * adsArray = [[NSMutableArray alloc] init];
......
NSMutableArray *array = [NSArray arrayWithContentsOfURL:url];
// convert the new fetchted dictionnaries of Ads to an Ad objects
for (NSMutableDictionary *dictAd in array)
{
// convertion using the designated initializer
Ad *ad = [[Ad alloc]initWithDictionary:dictAd];
[adsArray addObject:ad];
}
....
return dictFinal ;
}
在 mu 控制器的其他地方,我将这个 fetch 方法称为:
// do request on async thread
dispatch_queue_t fetchQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(fetchQ, ^{
NSDictionary * dict = [Ad fetchAdOfPage:currentPage PerPage:kAdsPerPage];
dispatch_sync(dispatch_get_main_queue(), ^{
.....
});
});
dispatch_release(fetchQ);
现在我的问题是,上次我在模拟器中运行应用程序时出现错误“集合在被枚举时发生了变异”,它指向以下行:
NSArray *temp = [ citiesDict allKeysForObject:cityNumberValue];
但是我第一次遇到这个错误,我之前没有得到它,我使用相同的代码三个多月,一切正常,我什至无法再次重现相同的错误!在放置 NSLog 以查看发生了什么后,我总是得到:
2013-05-21 19:39:20.141 myApp[744:3b03] loading plist
2013-05-21 19:39:20.151 myApp[744:6303] will return value
2013-05-21 19:39:20.151 myApp[744:3b03] will return value
2013-05-21 19:39:20.152 myApp[744:6303] will return value
2013-05-21 19:39:20.153 myApp[744:6303] will return value
2013-05-21 19:39:20.154 myApp[744:6303] will return value
2013-05-21 19:39:20.153 myApp[744:3b03] will return value
2013-05-21 19:39:20.155 myApp[744:6303] will return value
但有一次我得到:
2013-05-21 19:39:20.141 myApp[744:3b03] loading plist
2013-05-21 19:39:20.142 myApp[744:6303] loading plist
2013-05-21 19:39:20.151 myApp[744:6303] will return value
2013-05-21 19:39:20.151 myApp[744:3b03] will return value
2013-05-21 19:39:20.152 myApp[744:6303] will return value
2013-05-21 19:39:20.153 myApp[744:6303] will return value
2013-05-21 19:39:20.154 myApp[744:6303] will return value
2013-05-21 19:39:20.153 myApp[744:3b03] will return value
2013-05-21 19:39:20.155 myApp[744:6303] will return value
该应用程序没有崩溃,但是自从我使用 if 语句检查后,出现了两次奇怪的“加载 plist”!所以我猜有两个线程已经进入:
if (!citiesDict)
同时,然后他们俩都设置了citiesDict字典,因为该字典可以用于
[ citiesDict allKeysForObject:cityNumberValue];
就在可能导致崩溃的 if 语句“枚举时集合发生突变”之后,这可能是真正的情景吗?
因为我无法再次重现错误我想知道是否添加:
@synchronized(citiesDict)
{
citiesDict = [[NSDictionary alloc ]initWithContentsOfFile:path];
NSLog(@"loading plist");
}
可以解决这个问题吗?您有什么建议可以更好地实现更安全的实现,以及当我们不得不使用来自不同线程的相同数组并且只能从不同的线程会导致问题或问题只是在同时写入时?预先感谢您的帮助