2010-07-30 30 views
5

我正在爲用戶輸入時給出結果的iPhone創建字典應用程序。我使用線程(NSThread)來更新UITableView,以便主線程不被阻塞。但是,當UITableView詢問數據源的行數(tableView:numberOfRowsInSection :)並返回,比如說10時,就會發生崩潰。然後它詢問數據源的單元格0-9(tableView:cellForRowAtIndexPath :)。但是當它要求單元7時,數據源已經改變了,現在它只有5行,從而導致崩潰。使用線程更新UITableView

這是我如何解決這個問題:

我在init()方法創建一個NSLock。

而且這裏是數據源的樣子:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 
    return [results count]; 
} 


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    static NSString *CellIdentifier = @"Cell"; 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
    if (cell == nil) { 
     cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 
    } 

    [lock lock]; 
    if (indexPath.row < [results count]) { 
     cell.textLabel.text = [results objectAtIndex:indexPath.row]; 
    } 
    [lock unlock]; 

    return cell; 
} 

這裏是我用來更新表的代碼:

[tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; 

它徹底解決了死機問題。不過,我認爲它可能效率不高,因爲數據源在每次詢問單元時都必須鎖定/解鎖。而我上面提到的情況並不經常發生。有沒有人有更好的想法如何有效地解決這個問題?

非常感謝!

+2

你不應該更新在另一個線程的UI。 – rickharrison 2010-07-30 20:06:45

+0

Rick,我必須在另一個線程中搜索結果,因爲搜索方法非常慢,我不希望它阻塞主線程。你能解釋一下如何在不使用其他線程的情況下實現這一目標嗎? – ifvc 2010-07-30 20:14:52

+0

使它更快。你究竟在尋找什麼?! – mvds 2010-07-30 20:17:51

回答

2

爲什麼你會爲此使用一個單獨的線程?搜索需要多長時間? 0.1秒?這與用戶停止鍵入並查看屏幕所用的時間相比如何?

不要過分複雜的東西! (如果您的搜索時間超過0.7秒並且無法進行優化,我會將其退回;-)

+0

說實話,大概需要0.2-0.3秒。 (使用SQLite/FTS3的原始搜索大約需要0.5秒,所以我使用純文本和二分搜索進行了優化)儘管它很小,但如果與更新表所需的時間相結合,它仍然足夠大,我感覺像鍵盤是「粘性」。 – ifvc 2010-07-30 20:33:57

+0

因此,您正在查看大約1百萬條記錄...我會專注於優化數據查找,這也將有益於用戶體驗。首先,如果用戶鍵入「b」,則沒有理由立即製作整個「b」列表。 – mvds 2010-07-30 21:11:54

+0

我試過用SQLite,即使我使用「LIMIT 1」,它也沒有什麼區別。我想這可能是因爲SQLite首先查找整個列表,然後對它進行排序(即使我沒有使用任何「ORDER BY」),並最終返回結果。 這就是爲什麼我切換到二分搜索,所以我可以查找下邊界和上邊界的索引,然後根據需要查詢結果。 – ifvc 2010-07-30 21:41:23

4

不要嘗試從後臺線程更新UI。不起作用。

+1

這就是爲什麼我使用[tableView performSelectorOnMainThread:@selector(reloadData)withObject:nil waitUntilDone:NO];並且它可以工作(從某種意義上說,沒有崩潰)。但是,我希望通過每次詢問單元時不使用鎖定/解鎖來更快地工作。 – ifvc 2010-07-30 20:42:48

1

這裏有一個有用的答案,這裏是我對另一個類似問題的回答。

如果不知道更多關於您的應用的信息,我認爲更好的解決方案之一是將數組保存在主線程中,並在其他線程需要進行更改時將其分派給它。像這樣:

dispatch_async(dispatch_get_main_queue(), ^{ 
       [array addObject:object]; 
       [tableView reloadData]; 
      }); 

當然,你可以使用dispatch API獲得更復雜的結果,但它確實爲你處理鎖定和一切。絕對比使用NSLock更優雅。它只適用於iOS 4或更高版本。