2013-05-21 77 views
0

我有一个类“广告”在这里我设置一个静态变量,我Ad.m内“收集了同时列举突变”方法如果没有加载,基本上加载使用其指数的城市名称的plist文件,并最终转化为paramater传递到城市的名称中的数字值:线程安全的,

+(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方法来将字典转换为广告对象wh ERE它使用类方法+ 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服务的地方调用该方法+ cityFromNumberValue获取信息的方法:cityNumberValue:里面一个for循环:

+(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 ; 
} 

和万亩控制器别的地方我把这个取梅索德这样的:

// 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 

应用程序没有崩溃,但有一个奇怪的“装载的plist”两次,因为我使用if语句进行了检查!所以我想这有两个线程都进入:

if (!citiesDict) 

在同一时间,然后他们两个甲肝设置citiesDict词典,自本词典可以

[ citiesDict allKeysForObject:cityNumberValue]; 

只是后使用可能导致崩溃的if语句“Collection被枚举时发生了变化”,这可以成为真正的senario吗?

,因为我不能再重现错误不知是否添加:

@synchronized(citiesDict) 
    { 
     citiesDict = [[NSDictionary alloc ]initWithContentsOfFile:path]; 
     NSLog(@"loading plist"); 
    } 

可以解决这个问题?你有什么建议可以更好地实现一个更安全的实现,以及当我们必须从不同的线程处理同一个数组,并且可以从数组中读取数组的内容时,我们通常如何避免“集合在被列举时发生了变化”错误不同的线程会导致问题,或者问题出在同一时间写入?预先感谢您的帮助

回答

2

当静态对象的工作,最好的做法是使用dispatch_once,这样能保证你的代码只执行一次,给你完整的线程安全

这也是一个很好的做法,有一个getter方法你静态物体

+ (NSDictionary *)getCitiesDict { 
    static dispatch_once_t pred; 
    static NSDictionary *citiesDict = nil; 

    dispatch_once(&pred, ^{ 
     NSString *path = [[NSBundle mainBundle] pathForResource:@"cities" ofType:@"plist"]; 

     citiesDict = [[NSDictionary alloc ]initWithContentsOfFile:path]; 
    }); 

    return citiesDict; 
} 

要访问它,只是去这样

[[OwnerClass getCitiesDict] valueForKey:@"key"]; // OwnerClass is the name of the class where this is defined 
+0

谢谢你的回答,但是我应该在我加载plist之前测试它是否已经设置避免我一直调用getter来加载它?或者我会通过执行'if(!citiesDict)'来检查我将从哪里调用getter? – iOSGeek

+0

不需要检查。 dispatch_once块在应用程序的整个生命周期中只执行一次,这意味着citiesDict只会创建一次,并且会在活着并准备就绪之后。只要打电话给[self getCitiesDict],你就会好起来 – Ismael

+0

再次感谢你的回放,就像你不介意最后一个问题一样,实际上还有其他一些处理城市的类方法,他们需要同一个字典和他们测试它是否没有加载,他们从plist中加载它们,并且他们可以在同一时间执行: 'if(!citiesDict) { NSString * path = [[NSBundle mainBundle] pathForResource:@“cities “ofType:@”plist“]; citiesDict = [[NSDictionary alloc] initWithContentsOfFile:path]; }' 是否安全,在使用dispatch_once实现getter解决方案后可否面临任何问题? – iOSGeek

1

您正确的轨道上。为了从同一个方法中实现该数组的线程安全的加载,同步,并把空校验它:

@synchronized(citiesDict) 
{ 
    if (!citiesDict) 
    { 
     citiesDict = [[NSDictionary alloc] initWithContentsOfFile:path]; 
     NSLog(@"loading plist"); 
    } 
} 

把那上面的代码到您在阅读前阵调用一个函数。基本上你想避免在加载时读取数组。

+0

谢谢你的答案,但它是确定的,如果有其他类methodes时装载的plist如果它没有设置,只是看了吗? – iOSGeek

2

@synchronized(citiesDist)不是最好的,但应该解决您的问题......反正在我看来初始化一次在一个线程安全的环境变量的最好办法以这种方式使用GCD:

static dispatch_once_t once; 
dispatch_once(&once, ^{ 
    citiesDict = [[NSDictionary alloc ]initWithContentsOfFile:path]; 
    NSLog(@"loading plist"); 
}); 
+0

所以我猜代码应该是: 'if(!citiesDict) { static dispatch_once_t once; dispatch_once(一次,^ { 的NSString *路径= [[一个NSBundle mainBundle] pathForResource:@ “城市” ofType:@ “的plist”]; citiesDict = [[NSDictionary中的alloc] initWithContentsOfFile:路径]; 的NSLog( @“loading plist”); }); }' – iOSGeek

+0

你不需要检查if(!citiesDict){},该块将只被调度一次 – Manu