2013-09-22 35 views
15

Vì vậy, tôi tạo tải của tôi trên các chủ đề chínhNSURLSession Chủ đề: Theo dõi download nhiều nền

NSURLRequest *request = [NSURLRequest requestWithURL:download.URL]; 
NSURLSessionDownloadTask *downloadTask = [self.downloadSession downloadTaskWithRequest:request]; 
[downloadTask resume]; 

và thêm các NSManagedContextID liên quan đến việc tải về một NSMutableDictionary, vì vậy tôi có thể lấy nó sau này trong các đại biểu call-lưng

[self.downloads setObject:[download objectID] forKey:[NSNumber numberWithInteger:downloadTask.taskIdentifier]]; 

My self.downloadSession trên được cấu hình như thế này

- (NSURLSession *)backgroundSession 
{ 
static NSURLSession *session = nil; 
static dispatch_once_t onceToken; 
dispatch_once(&onceToken, ^{ 
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.test.backgroundSession"]; 
    configuration.discretionary = YES; 
    session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; 
}); 
return session; 
} 
.210

Vấn đề của tôi là callbacks đại biểu dường như kêu gọi các chủ đề khác nhau

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite 
{ 
    NSManagedObjectID *downloadID = [self.downloads objectForKey:[NSNumber numberWithInteger:downloadTask.taskIdentifier]]; 

    double progress = (double)totalBytesWritten/(double)totalBytesExpectedToWrite; 

    NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:downloadID,@"download",[NSNumber numberWithDouble:progress],@"progress", nil]; 

    [[NSNotificationCenter defaultCenter] postNotificationName:@"DownloadProgress" object:nil userInfo:userInfo]; 

} 

Vì vậy, khi tôi truy cập self.downloads để có được những ObjectId đúng, tôi thực sự truy cập vào NSMutableDictionary từ một thread khác nhau hơn so với cái nó đã được tạo ra trên, và tôi tin rằng NSMutableDictionary không phải là chủ đề an toàn. Vì vậy, là những gì các giải pháp tốt nhất cho việc này, tôi có thể sử dụng một cái gì đó như thế này

session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]]; 

khi tuyên bố phiên giao dịch, thiết lập các hàng đợi đại biểu đến mainQueue mà gây ra tất cả các đại biểu được gọi vào các chủ đề chính, nhưng tôi sẽ muốn giữ tất cả các cuộc gọi lại trên một chuỗi nền nếu có thể

+0

Bạn đã thử chỉ cần đặt chuỗi của riêng mình khi xác định phiên? – Mundi

+0

@Mundi Điều gì về việc xử lý với mô hình 'NSMutableArray' và' NSObject' như được giải thích trong [hướng dẫn này?] (Http://www.appcoda.com/background-transfer-service-ios7/) – Praveenkumar

Trả lời

10

Trong ví dụ của bạn không phải là vấn đề, vì từ điển của bạn được chuyển sang hệ thống thông báo và không được sử dụng lại bởi chuỗi xếp hàng hoạt động. Chủ đề an toàn chỉ là một vấn đề khi một đối tượng có khả năng truy cập từ nhiều luồng cùng một lúc.

Nếu dict của bạn sẽ là một Ivar bạn nên làm theo cách này:

Tạo hàng đợi riêng của bạn như thế này

myQueue = [[NSOperationQueue alloc] init]; 
// This creates basically a serial queue, since there is just on operation running at any time. 
[myQueue setMaxConcurrentOperationCount:1]; 

Sau đó sắp xếp tất cả các truy cập vào từ điển của bạn trên hàng đợi này, như thế này ví dụ :

[myQueue addOperationWithBlock:^ 
{ 
    // Access your dictionary 
}]; 

Và dĩ nhiên sử dụng hàng đợi này để đoàn URLSesson của bạn:

session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:myQueue]; 

Vì hàng đợi này được thiết lập như một hàng đợi nối tiếp, sẽ luôn có một chủ đề truy cập dict trong nền.

Hãy cẩn thận khi bạn tính toán điều gì đó với thông tin dict. Bạn cũng phải làm điều này trên hàng đợi đó. Tuy nhiên, bạn có thể đặt kết quả tính toán của mình trên bất kỳ hàng đợi/chuỗi khác, ví dụ để cập nhật giao diện người dùng trên chuỗi chính.

[myQueue addOperationWithBlock:^ 
{ 
    // Calculate with your dictionary 
    // Maybe the progress calcualtion 
    NSString* progress = [self calculateProgress: iVarDict]; 
    dispatch_async(dispatch_get_main_queue(),^
    { 
     // use progress to update UI 
    }); 
}]; 

Tôi nghĩ rằng khi đăng thông báo bạn không phải sử dụng mẫu đó, vì hệ thống xử lý luồng chính xác. Nhưng để cứu bạn nên kiểm tra điều này.

+0

Vâng, tài liệu nói nó không phải là: [Threading Prgramming Guide] (https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html) – sofacoder

+0

Điều gì về việc xử lý với 'NSMutableArray' và' NSObject' mô hình của như được giải thích trong [hướng dẫn này?] (http://www.appcoda.com/background-transfer-service-ios7/) – Praveenkumar

0

Bạn có thể sử dụng hàng đợi nối tiếp GCD để đảm bảo chỉ có một đại biểu thực hiện đồng thời.

Bạn có thể khai báo hàng đợi như là một biến thể hiện của lớp học của bạn và khởi tạo nó trong phương thức init, như thế này:

dispatch_queue_t delegateQueue; 

...

delegateQueue = dispatch_queue_create("com.yourcompany.mydelegatequeue", 0x0); 

và trong phương pháp đại biểu của mình, chỉ cần làm cho nó thực hiện trong hàng đợi này:

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite 
{ 
    dispatch_sync(delegateQueue, ^{ 
    NSManagedObjectID *downloadID = [self.downloads objectForKey:[NSNumber numberWithInteger:downloadTask.taskIdentifier]]; 

    double progress = (double)totalBytesWritten/(double)totalBytesExpectedToWrite; 

    NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:downloadID,@"download",[NSNumber numberWithDouble:progress],@"progress", nil]; 

    [[NSNotificationCenter defaultCenter] postNotificationName:@"DownloadProgress" object:nil userInfo:userInfo]; 
}); 

} 

Bằng cách này, mặc dù mọi đại biểu được gọi là i n chủ đề của nó, chỉ có họ truy cập self.downloads cùng một lúc, và bạn có thể giữ chúng trong các chủ đề riêng biệt.

+0

Điều này là hơi tối ưu, vì như đã nêu trong tài liệu NSURLSession tạo ra một hàng đợi nối tiếp khi delegateQueue được đặt thành 0. Vì vậy, các cuộc gọi đại biểu được thực thi trên hàng đợi đó và sau đó mã ngay lập tức chuyển sang hàng đợi khác. Một hàng đợi (và có ít nhất một luồng bị lãng phí) có thể dễ dàng tránh được. Bạn có thể đặt NSURLSession thành mainQueue, nhưng điều này một lần nữa sẽ là thời gian mà hàng đợi không cần thiết. – sofacoder

+0

Vì anh ấy đăng thông báo ngay lập tức, sẽ thông minh hơn để bọc toàn bộ nội dung trong công văn trên chuỗi chính. – Andy

Các vấn đề liên quan