2011-11-21 22 views
1

我正在研究一個iPad應用程序,該應用程序具有用於滾動瀏覽數據的滑塊。滾動時,顯示地圖並更新數據。問題是,如果你滾動得足夠快(或以某種方式觸發競爭條件),應用程序崩潰訪問殭屍NSString。我已經能夠跟蹤它在探查,發現這個:應用程序在繪製UILabel時崩潰 - NSString是一個殭屍

Event Type RefCt Timestamp  Size Responsible Library  Responsible Caller 
Malloc  1  01:55.166.466 16  Foundation    -[NSPlaceholderString initWithFormat:locale:arguments:] 
Autorelease <null> 01:55.166.472 0  Foundation    +[NSString stringWithFormat:] 
CFRetain 2  01:55.166.473 0  My Program    -[StateView updateVotes:] 
CFRetain 3  01:55.166.476 0  UIKit     -[UILabel setText:] 
CFRelease 2  01:55.166.504 0  My Program    -[StateView updateVotes:] 
CFRelease 1  01:55.177.661 0  Foundation    -[NSAutoreleasePool release] 
CFRelease 0  01:55.439.090 0  UIKit     -[UILabel setText:] 
Zombie  -1  01:55.439.109 0  UIKit     -[NSString(UIStringDrawing) drawAtPoint:forWidth:withFont:lineBreakMode:letterSpacing:includeEmoji:] 

我使用ARC上的iOS5,所以我在保留/釋放的控制是根本。即使我是,看着上述,這是正確的。問題似乎是繪圖函數和實際更改的UILabel字符串之間的競爭條件。 UILabel釋放第一個字符串,因爲已經設置了一個新的字符串,但繪圖函數以某種方式持有對它的引用,但沒有保留它。

作爲說明,我沒有以任何方式修改UILabel。

任何想法?

---代碼添加更新:

滑塊更新:

-(void)sliderValueChanged:(UISlider *)slider { 
    float position = slider.value - 1790.0f; 
    int year; 
    if(position <= 0.0f) { 
     year = 1789; 
    } else { 
     year = 1792 + (floor(position/4.0f)*4); 
    } 
    [self setYear:year]; 
} 

setYear:

-(void)setYear:(int)year { 
if (year == currentYear) { 
     // year didn't change, so don't do anything 
     return; 
    } 

    [yearLabel setText:[[NSString alloc] initWithFormat:@"%i", year]]; 
    currentYear = year; 

    [self getMapForYear:year]; 
} 

getMapForYear:

-(void) getMapForYear:(int)year { 
    [self setToMap:[historicalData objectForKey:[NSNumber numberWithInt:year]]; 
} 

setToMap:

-(void) setToMap:(HistoricalMap *)map { 
    // Label the map 
    for (State *state in [map states]) { 
     [mapController setVotes:[state votes] forState:[state abbreviation]]; 
    } 
} 

setVotes:forState:

-(void)setVotes:(NSNumber *)votes forState:(NSString *)stateAbbreviation { 

    StateView *state = [states objectForKey:stateAbbreviation]; 
    if (state == nil) { 
     NSLog(@"Invalid State Votes -- %@", stateAbbreviation); 
     return; 
    } 
    [state updateVotes:votes]; 
    [state setNeedsDisplay]; 
} 

updateVotes:

-(void)updateVotes:(NSNumber *)newVotes { 
    [self setVotes:newVotes]; 

    NSString *voteString = [[NSString alloc] initWithFormat:@"%@", newVotes]; 

    [voteLabel setText:voteString]; 
    if ([newVotes isEqual:[NSNumber numberWithInt:0]]) { 
     [[self voteLabel] setHidden:YES]; 
     [[self stateAbbreviationLabel] setHidden:YES]; 
    } else { 
     [[self stateAbbreviationLabel] setHidden:NO]; 
     [[self voteLabel] setHidden:NO]; 
    } 
} 
+0

請包括你在哪裏更新數據作爲滑塊值發生變化的代碼創建字符串。請記住,如果滑塊是連續的,則可以每秒調用數百次。 – jrturton

+0

這是否有任何線程?上面顯示的內容不應該像你描述的那樣行爲,在繪製之前將字符串釋放,除非在另一個地方有一個不同的線程將地毯拉出。我不知道只有一個線程涉及到這種競爭條件。 –

回答

1

我認爲你正試圖滑塊的運動過程中做太多。單獨創建和執行核心數據提取請求看起來是過度的,更不用說更新整個GUI和一個標籤屏幕了。您是否在設備上測試過這種性能?

可能值得剖析這些代碼段並查看時間花在哪裏。例如,您可以查看緩存提取請求或結果,或者只有在滑塊已停止時,或者只在路徑上每增加一個增量時,您纔可以進行更新。

+0

就表現而言,它運行良好。我已將核心數據調用移出滑塊更新並將其存儲在控制器中。更新速度更快,但我遇到了同樣的問題。 – bertuccio255

+0

我想我已經通過在覈心數據管理對象中創建和存儲字符串來解決問題。這種方式只要核心數據對象存在就存在字符串。感謝您的幫助。欣賞改善表現的想法。 – bertuccio255

+0

好吧,很高興你的工作 - 它聽起來像一個很酷的效果,擦洗歷史! – jrturton

0

您havw幾個內存泄漏部件的NSString:

[yearLabel setText:[[NSString alloc] initWithFormat:@"%i", year]]; // leak 

stringWithFormat方法,而不是

[yearLabel setText:[NSString stringWithFormat:@"%i", year]]; 
+0

謝謝你指出。我改變了它試圖追蹤殭屍,並沒有將其切換回來。 – bertuccio255

+0

@ bertuccio255 - 其實,如果你在這裏使用ARC,兩者之間應該沒有區別。你不應該泄漏任何東西。 –

0
[NSString stringWithFormat: **is the best way formatting the string than any other..** 
+0

正如我指出的鈹的答案,在ARC下沒有'+ stringWithFormat:'和'-initWithFormat:'之間的有效區別。另外,聲稱'+ stringWithFormat:'在所有情況下都是最好的,這是一個相當廣泛的陳述。 –