2013-05-21 111 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