1

因此,在瞭解完成塊之後,我很喜歡使用完成塊。我喜歡關閉,我喜歡在任何地方傳遞任何想要的東西的能力。關於使用完成塊排隊

作爲線程編程的新手,我一直在遠離GCD和NSOperation - 但最近我不得不編程異步更新到核心數據的東西,我開始懷疑我的「全部完成阻止所有的時間「的方法。

所以這裏是我自問的一個例子:我有一系列可能相當大的數據(圖像,聲音,視頻,你有什麼)上傳到服務器的地方。這些數據的元數據存儲在覈心數據中,並且我有一個時間戳用於決定應該上傳哪些對象。所有這些上傳應該按順序進行。

我所編碼的是一種本質上基本上是一個在它完成塊,在該塊的一端有一個電話給自己,這樣的功能:

(void)uploadAllAsynchronously { 
    ... // First figure out what to upload based on core data 
    // Here comes the completion block in question 
    void(^blk)(BOOL) = ^(BOOL)uploadSuccess { 
    ... // if upload successful, update core data to mark what has been uploaded 
    [self uploadAllAsynchronously]; // Recursively calls the function that contains this block. I actually have a weak self, or if that fails to break a retain cycle, I should be able to pass in a NSManagedObjectContext as an argument. 
    } 
    [NSURLConnection sendAsynchronousRequest:... queue:... completionHandler:blk]; 


} 

這應該工作,對不對?有沒有什麼東西是完全危險的,這表明我必須使用GCD並處理我自己的隊列?我問這是因爲我現在有點麻煩,可能數據中會出現不同的線程,由於異步調用而不能正確更新,但不知道我的代碼的哪一部分是罪魁禍首。

在此先感謝。

+0

請記住,完成處理程序通常不會在後臺線程上運行。通常(包括'[NSURLConnection sendAsynchronousRequest:queue:completionHandler:]')完成塊在主線程上執行。所以你根本不用擔心線程安全。如果主線程在後臺線程完成下載時處於繁忙狀態,則在執行完成塊之前,主線程將處於空閒狀態。 –

+0

我發現有一種情況嵌套完成塊會有問題,並可能導致死鎖:http://www.cocoawithlove.com/2010/06/avoiding-deadlocks-and-latency-in.html – Victor

回答

1

你塊是錯誤的類型。

至於the documentation

+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse *, NSData *, NSError *))handler 

顯示,完成黑色的類型是

void (^) (NSURLResponse *, NSData *, NSError *) 

void (^) (BOOL) 

你應該改變blk是像

void (^blk) (NSURLResponse *, NSData *, NSError *) =^(NSURLResponse *response, NSData *data, NSError *error) { 
    //... 
}; 

這將是更時尚寫

[NSURLConnection sendAsynchronousRequest:theRequest queue:theQueue completionHandler:^ (NSURLResponse *response, NSData *data, NSError *error) { 
    //... 
}]; 

帶有線上與方法完成塊。


關於執行操作上的問題,您NSManagedObjectContext在完成處理程序:這是好的,只要傳遞給sendAsynchronousRequest:queue:completionHandler:NSOperationQueue是一樣的,其中創建的託管對象上下文的一個。但隨着the documentation for NSManagedObjectContext狀態

核心數據使用線程(或序列化隊列)約束,以保護管理對象和管理對象上下文(請參閱「併發核心數據」)。其結果是上下文假定默認所有者是分配它的線程或隊列 - 這由調用其init方法的線程決定。因此,您不應該在一個線程上初始化上下文,然後將其傳遞給另一個線程。相反,您應該傳遞對持久存儲協調器的引用,並讓接收線程/隊列創建一個從此派生的新上下文。

如果您傳遞的隊列是不是您所創建的管理對象範圍內的人,你必須做的隊列在管理對象方面是以下

  1. 呼叫-[NSOperationQueue addOperation:]之一創建。

  2. 在發生核心數據操作的隊列上創建第二個託管對象上下文(使用相同的持久存儲協調器)。

  3. 在發生核心數據操作的隊列上創建第二個管理對象上下文和第二個持久存儲協調器。

  4. 使用鎖定。

Concurrency with Core Data的文件清楚地表明,你必須使用線程約束(以上選項1-3)或使用鎖定(上面的選項4)。

這就是文檔必須說關於使用鎖:

如果您選擇不使用線程容納圖案,也就是說,如果你試圖在線程之間傳遞管理對象或情境,等等你必須非常小心鎖定,因此你可能會否定任何你可能從多線程中獲得的好處。

這就是文檔不得不說具有不只是每個線程的管理對象上下文也每個線程的持久性存儲協調員:

這種做法在更大的代價提供了更大的併發性複雜性(特別是如果您需要在不同的上下文之間傳遞更改時)和增加的內存使用率。

+0

我的主要事情因此文檔是這樣的:在WWDC 2012 Session 214視頻中,它看起來像「因此,不應該在一個線程上初始化上下文,然後將其傳遞給另一個線程」不再適用於每個上下文(只要它是MainQueue或PrivateQueue之一)實際上管理自己的隊列,不管它是什麼線程。我不知道你是否看過那段視頻,但如果你有,我是否正確解釋了視頻中的主持人? – Victor

+0

我目前無法訪問該WWDC視頻,但有關[核心數據併發性](https://developer.apple)的文檔。com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdConcurrency.html#// apple_ref/doc/uid/TP40003385)很清楚,如果您使用多個線程/隊列,則必須使用線程限制(具有每個線程管理的對象上下文(以及潛在的每線程持久存儲協調器))或使用鎖定。 (請參閱我的編輯。) –

+0

是的,這就是我感到困惑 - 文檔似乎並不像我在WWDC視頻中的解釋一樣。我對視頻的印象(我認爲這是最近的)說,現在覈心數據處理自己的隊列,所以除非絕對必要,否則不要在覈心數據呼叫中使用GCD。或者我認爲 - 在任何操作系統的線程中,我都是新手。 – Victor

1

是此代碼應工作..注:我會重新命名方法的話.. uploadIfNeeded可能 - 因爲它不總是一味上傳東西...