2010-04-25 46 views
9

我試圖從網站xml中獲取數據。一切正常。異步調用目標C iphone

但UIButton保持按下狀態,直到xml數據被返回,因此如果這些與互聯網服務有關的問題,它不能被糾正,應用程序幾乎不可用。

這裏的來電:

{ 
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 
    if(!appDelegate.XMLdataArray.count > 0){ 
     [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; 
     [appDelegate GetApps]; //function that retrieves data from Website and puts into the array - XMLdataArray. 

    } 
    XMLViewController *controller = [[XMLViewController alloc] initWithNibName:@"MedGearsApps" bundle:nil]; 
    [self.navigationController pushViewController:controller animated:YES]; 
    [controller release]; 
} 

它工作正常,但我怎樣才能使視圖按鈕功能讓滯留。換句話說,我只想讓UIButton和其他UIButtons在後臺運行時起作用。

我聽說performSelectorInMainThread,但我不能把它練正確

任何幫助表示讚賞:)

回答

36

你不太瞭解線程模型,如果你開始添加異步代碼而沒有真正理解正在發生的事情,你可能會在自己的腳下開槍。

您編寫的代碼在主應用程序線程中運行。但是當你考慮它的時候,你不需要寫任何main函數 - 你只需實現應用程序委託和事件回調(例如觸摸處理程序),並在某些時候自動運行。這不是一個魔術,這只是一個叫做Run Loop的可可物件。

運行循環是一個接收所有事件,處理計時器(如NSTimer)並運行您的代碼的對象。這意味着,當你,例如,做一些事情,當用戶點擊一個按鈕,調用樹看起來有點像這樣:

main thread running 
    main run loop 
     // fire timers 
     // receive events — aha, here we have an event, let’s call the handler 
     view::touchesBegan… 
      // use tapped some button, let’s fire the callback 
      someButton::touchUpInside 
       yourCode 

現在yourCode做你想要做什麼和運行循環繼續運行。但是當你的代碼需要很長時間才能完成時,比如你的情況,運行循環必須等待,因此直到你的代碼完成之後,事件纔會被處理。這是您在應用程序中看到的內容。

要解決這種情況,您必須在另一個線程中運行長操作。這並不是很難,但是你必須考慮一些潛在的問題。運行在另一個線程可以那麼容易,因爲調用performSelectorInBackground

[appDelegate performSelectorInBackground:@selector(GetApps) withObject:nil]; 

現在你擁有的數據已經被加載到想辦法告訴應用程序,如使用的通知或調用選擇主線。順便說一句:將數據存儲在應用程序委託中(或者甚至使用應用程序委託來加載數據)並不是非常優雅的解決方案,但這是另一回事。

如果您選擇performSelectorInBackground解決方案,請查看有關memory management in secondary threads的相關問題。你需要你自己的autorelease池,這樣你纔不會泄漏autoreleased對象。


更新答案一段時間後 - 現在它通常最好使用運行大中央調度在後臺代碼:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    // No explicit autorelease pool needed here. 
    // The code runs in background, not strangling 
    // the main run loop. 
    [self doSomeLongOperation]; 
    dispatch_sync(dispatch_get_main_queue(), ^{ 
     // This will be called on the main thread, so that 
     // you can update the UI, for example. 
     [self longOperationDone]; 
    }); 
}); 
+0

是的,我確實瞭解了這些東西,但仍然有很多東西需要理解,但我會明白的,你已經把我放在了正確的方向。謝謝。我正在使用TBXML組件,所以,我想我需要寫一個簡單的瞭解它是如何工作的。 無論如何,另一個相關的問題是,如果用戶選擇關閉視圖,是否有終止selectorInBackground調用呢?在ViewDidUnload ..? – Sam 2010-04-26 14:27:49

+1

這取決於。在簡單情況下,您可以忽略該問題並對通知部分進行編碼,以便在視圖被提前解除時不會中斷。使用異步'NSURLConnection'模式或操作隊列可以寫出更加防彈的解決方案,它們都有一個方法來取消正在進行的操作。 – zoul 2010-04-26 17:13:07

+0

最後一段代碼非常棒,這正是我所需要的。非常感謝! – kisileno 2015-05-17 21:56:23

0

您可以使用該被壓入操作隊列後臺操作的:

BGOperation *op = [[BGOperation alloc] init]; 
[[self operationQueue] addOperation:op]; 
[op release]; 

我已經創建了在後臺執行的特定「命令」:

@implementation BGOperation 

# pragma mark Memory Management 

- (BGOperation *)init 
{ 
if ((self = [super init]) != nil) 
    /* nothing */; 
return self; 
} 

- (void)dealloc 
{ 
self.jobId = nil; 
[super dealloc]; 
} 

# pragma mark - 
# pragma mark Background Operation 

- (void)main 
{ 
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    [appDelegate GetApps]; 
[pool release]; 
return; 
} 

@end

完成後,發送通知到主線程可能是一個好主意,因爲內部數據庫已更改。

+1

是不是更容易使用'performSelectorInBackground'或異步' NSURLConnection'模式? – zoul 2010-04-25 19:09:00

+1

不,因爲那你必須處理線程生命週期(在給定的例子中,他不需要創建他自己的autorelease池)。 Asynch NSUrlConnection是可以的,但它可以在主線程中工作,所以可以讓你的UI口吃,特別是如果你一次只有幾個。 OperationQueues是遠遠更容易處理和管理... – 2010-04-25 19:26:45

+0

創建和耗盡自動釋放池是兩行代碼,它對我來說似乎不是什麼大不了的事情。 (只是一個觀點。) – zoul 2010-04-25 19:42:29

0

看起來好像您可能在getApps方法中使用NSURLConnection。如果是這樣,您應該將其轉換爲異步調用。

3

使用NSURLConnection的connectionWithRequest:delegate:方法。這將導致指定的請求被異步發送。代表應該響應連接:didReceiveResponse:並且一旦完整接收到響應,將發送該消息。