2011-12-08 56 views
13

如何讓我的程序在轉到下一行代碼之前等待異步NSURLConnection完成?如何讓程序在繼續執行下一個命令之前等待異步NSURLConnection完成?

SetDelegate *sjd= [SetDelegate alloc]; 
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:post delegate:sjd]; 
[connection start]; 

我這是怎麼開始的連接,我辦理的委託接收到的數據,但我想等待連接到主要出發前結束,因爲這是一個for循環,它具有爲每個運行元素在我的數據庫中。

我需要將電話數據庫中的數據放到遠程數據庫,並且數據成功放入電話數據庫中的數據後將被刪除。我正在瀏覽手機數據庫中的每個元素,並開始一個連接,這就是爲什麼我看不到循環中可以完成的下一個任務。我是一個初學者,當談到Objective-C編程,所以我不知道這是正確的方式還是不這樣做

使調用同步不是一個選項,因爲它阻止了程序,我有應該顯示的進度條。

+1

任何理由,你爲什麼不能簡單地做,從委託,而不是正確的,在循環「下一步」的東西? :) –

+0

是否避免以並列方式啓動許多請求?如果是這樣,你的委託方法可以負責從服務器獲取下一個結果。有很少的情況下,你真的想阻止HTTP請求,特別是如果你正在做很多事情。 –

+0

我遇到了與OP相同的情況,我有一個應用程序在後臺線程中大量使用同步調用,現在想要支持使用異步使用的自定義身份驗證(使用NSURLConnection的委託方法)。相反,重寫應用程序,我可以「包裝」異步調用,基本上使它們同步,因爲它們已經發生在後臺線程上,一切都很好。 – Aaron

回答

19

你的問題有點奇怪。你不可能限制這個問題。你不能讓一行代碼「等待」一個進程完成而沒有阻塞某些進程,在這種情況下,無論循環運行什麼線程。

如果你想使用同步調用,它不會不會阻止你的應用程序,它只會阻止它被執行的線程。在你的例子中,你有一個持續獲取遠程數據的循環,你希望你的UI能夠反映出來,直到完成。但你不希望你的用戶界面被阻止。這意味着,你的循環線程已經必須在後臺線程,所以你可以隨意在沒有阻塞你的UI線程的循環中進行同步調用。如果循環在UI線程上,你需要改變它來做你想做的事情。

您也可以使用異步連接執行此操作。在這種情況下,您的操作可能實際完成更快的銀行,您可以同時處理多個請求。如果你這樣做,你的循環可以保持在UI線程上,你只需要跟蹤所有的連接,這樣當它們完成後,你可以將這個狀態傳遞給相關的控制器。您需要iVars來跟蹤加載狀態,並在加載完成後使用協議或NSNotification進行通信。

編輯:同步調用後臺線程,將實施例

如果您希望循環完全只完成時,所有的請求都完成並不能阻止這裏的UI線程是一個簡單的例子:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    // post an NSNotification that loading has started 
    for (x = 0; x < numberOfRequests; x++) { 
     // create the NSURLRequest for this loop iteration 
     NSURLResponse *response = nil; 
     NSError *error = nil; 
     NSData *data = [NSURLConnection sendSynchronousRequest:request 
              returningResponse:&response 
                 error:&error]; 
     // do something with the data, response, error, etc 
    } 
    // post an NSNotification that loading is finished 
}); 

無論對象需要顯示加載狀態,都應該觀察並處理您在此處發佈的通知。循環將通過後臺線程同步完成所有請求,並且您的UI線程將被解除阻塞並進行響應。這不是唯一的方法,事實上,我會自己使用異步連接,但這是一個簡單的例子,說明如何獲得你想要的。

+0

@pillblast,所有應用程序執行你要做的事情(下載一些東西並讓UI響應並反映狀態之前,期間和之後)通過保持UI線程暢通並且異步執行加載操作。你可以通過使用異步NSURLConnection或上面的示例來做到這一點,後者在後臺線程上執行同步請求。主線程(UI)上沒有阻塞線程的同步操作。 – XJones

+0

謝謝你的回答。對不起,在賞金結束之前我不能選擇它作爲答案,但它與聖誕節假期相交。我按照你的建議使用後臺線程完成了它,它的工作原理與我想要的完全一樣。我只是在動作發生時禁用Tab控制器,一切都很好。 – Pillblast

+0

在聯網完成之前,您應該使用下面的建議,而不是阻止UI。如果您使用像傑瑞建議的通知,則您的用戶界面不會被阻止,只有在網絡連接完成後纔會更新。 – anders

-1

NSURLConnection已經是異步的。只需實現委託方法。如果您想更新的MainThread(例如進度條)用戶界面,您可以在didReceiveData. 這樣做或this

+0

我的問題是關於使它幾乎同步,所以我不明白你的答案 – Pillblast

3

看,如果你真的想要等待,爲什麼要使用異步調用呢?請使用同步呼叫代替:

NSURLResponse* response = nil; 
NSData* data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:nil] 

此方法將阻止執行的線程,因此您應該確保要執行此操作!我提到它會阻止嗎? :)

+0

那麼,這就是爲什麼我不想使用同步調用,因爲它會阻止應用程序。這不是一個選項。 – Pillblast

+0

所以你想要發生一系列的下載,有一個進度指示器,並允許用戶做其他事情,更正?一個可能的途徑是採取這整個行動,並將其納入NSOperation。您是否應該在進度條上顯示每個單獨下載的進度或下載集?你下載了多少項目?它們有多大? –

+0

他們不是很大,但他們可能是數百人。問題是,即使在NSOperation中,我也會遇到同樣的問題,我現在在for循環中使用異步NSURlConnection。 – Pillblast

1

你說你想等待異步調用完成,所以我假設你正在調用你在一個單獨的線程發佈的代碼。

我建議看看新的sendAsynchronourRequest方法。我有posted up an example of how you can wrap this up in a class,當連接完成/超時/失敗時,它會通知它的委託人。我只是把你引用到這篇文章中,因爲它聽起來像是在嘗試實現與我當時所做的非常相似的事情,而這個DownloadWrapper類對我來說完美無瑕。這是iOS5中的新功能,請注意。

5

如果您只是想知道它何時完成並且不關心任何數據,只需使用NSNotificationCenter發佈通知並讓您的視圖訂閱它。

代表 - 完成後發佈通知

-(void) connectionDidFinishLoading:(NSURLConnection*)connection { 
    [[NSNotificationCenter defaultCenter] postNotificationName:@"NSURLConnectionDidFinish" object:nil]; 
} 

查看 - 添加觀察者和觀察

-(void) viewDidLoad { 

[[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(yourCleanupMethod) 
               name:@"NSURLConnectionDidFinish" 
               object:nil]; 
} 

-(void) yourCleanupMethod { 
    // finish up 

    [[NSNotificationCenter defaultCenter] removeObserver:self]; 
} 

現在,當運行一些代碼,如果你需要通過一個簡單的對象追溯到數據,你可以嘗試加載你的通知中的對象參數,如下所示:

[[NSNotificationCenter defaultCenter] postNotificationName:@"NSURLConnectionDidFinish" object:yourDataObject]; 

然後改變你的觀點和清理簽名是這樣的:

-(void) viewDidLoad { 

// Notice the addition to yourCleanupMethod 
[[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(yourCleanupMethod:) 
               name:@"NSURLConnectionDidFinish" 
               object:nil]; 
} 

-(void) yourCleanupMethod:(NSNotification *)notif { 
    // finish up 
    id yourDataObject = [notif object]; 

    [[NSNotificationCenter defaultCenter] removeObserver:self]; 
} 

現在我發現自己需要的東西比這多一點,所以我結束了創建一個單身處理我所有的請求。由於NSURLConnectionDelegate中的所有代理方法都爲您提供了特定連接的NSURLConnection實例,因此您可以將簡單的數據對象存儲在字典中,並且每次都可以通過連接查找它。從那裏我有一個方法簽名,採取和對象和選擇器中,我與連接關聯,所以在所有事情已經結束後,我可以通過在該對象上執行選擇器將該可變數據對象傳遞給請求者。

我不會在這裏包含所有的代碼,但也許這會幫助你思考什麼是可能的。我發現我在編寫Web服務調用時遇到了很多代碼,因此將所有東西封裝在一個單例中給了我一個很好的獲取數據的簡潔方法。希望這可以幫助!

+1

當事件發生時,通知對於創建「回叫」非常有幫助。我會建議這種方法 – anders

1

This金塊幫助我! 我正在使用同步NSURL很好,直到我決定我需要SSL爲我的客戶端和我的服務器之間的連接。我採用了key pinning的方法,該方法將設備上的證書與服務器上的證書進行比較(請參閱上面的鏈接),爲了使其工作,我需要將代碼添加到NSURL方法中,從我的研究中可以與NSURL不同步。 直到我發現了這個可笑的簡單解決方案,它爲我工作:

NSString *connectionRunLoopMode = @"connectionRunLoopMode"; 
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:urlRequest delegate:urlConnectionDelegate startImmediately:NO]; 
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop]; 
[connection unscheduleFromRunLoop:currentRunLoop forMode:NSDefaultRunLoopMode]; 
[connection scheduleInRunLoop:currentRunLoop forMode:connectionRunLoopMode]; 
[connection start]; 
while ([currentRunLoop runMode:connectionRunLoopMode beforeDate:[NSDate distantFuture]]); 
+0

while line引發sigabrt錯誤,當我運行這個 – Aaron

+0

@Aaron閱讀以下內容:http://stackoverflow.com/questions/8072135/how-to-track-down-cause-of-sigabrt – ConfusedDeer

+1

我最終將這段代碼縮短爲'NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:urlConnectionDelegate];我的同步調用(在後臺線程上)變成支持身份驗證委託的異步調用,它允許我將同步調用(在後臺線程上)轉換爲支持身份驗證委託的異步調用方法,同時仍然是同步的,以免重大更改我的應用程序。 – Aaron

相關問題