2013-03-29 26 views
5

Ứng dụng tôi đang làm việc định kỳ làm mới bộ nhớ cache dữ liệu cục bộ từ máy chủ ứng dụng (hơn 10 yêu cầu, mỗi yêu cầu mất một lượng thời gian hợp lý). Tôi hiện đang chạy các yêu cầu này một cách không đồng bộ để không chặn luồng giao diện người dùng. Vì các yêu cầu này mất một thời gian để xử lý và sau đó tải vào dữ liệu cốt lõi, tôi muốn tận dụng hành vi hoạt động phụ thuộc của beginBackgroundTaskWithExpirationHandler và hoạt động phụ thuộc của NSOperationQueue.NSOperationQueue waitUntilAllOperationsAreFinished không hoạt động khi đang ở chế độ nền

Sau khi tôi đã thêm tất cả các yêu cầu vào hàng đợi hoạt động, tôi đang sử dụng waitUntilAllOperationsAreFinished để chặn cho đến khi tất cả các thao tác được hoàn thành (điều này không nằm trong chuỗi chính). Vấn đề tôi thấy trong nguyên mẫu của tôi là khi tôi chạy ứng dụng và ngay lập tức nền nó (nhấn nút home), các waitUntilAllOperationsAreFinished vẫn bị chặn ngay cả sau khi tất cả các hoạt động đã hoàn thành ... nhưng ngay sau khi tôi mở ứng dụng một lần nữa , xử lý kết thúc. Nếu tôi chạy ứng dụng và để nó ở lại nền trước, mọi thứ sẽ kết thúc tốt đẹp. Hành vi này dường như không xảy ra khi ở trong ứng dụng thực tế của tôi, nhưng với mã ví dụ bên dưới, có vẻ như:

#import "ViewController.h" 

@interface ViewController() 

@property (assign, nonatomic) UIBackgroundTaskIdentifier task; 
@property (strong, nonatomic) NSOperationQueue *queue; 

@end 

@implementation ViewController 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    [self performSelectorInBackground:@selector(queueItUp) withObject:nil]; 
} 

- (void)queueItUp { 
    UIApplication *application = [UIApplication sharedApplication]; 

    self.queue = [[NSOperationQueue alloc] init]; 
    self.task = [application beginBackgroundTaskWithExpirationHandler:^{ 
     NSLog(@"Took too long!"); 

     [self.queue cancelAllOperations]; 
     [application endBackgroundTask:self.task]; 
     self.task = UIBackgroundTaskInvalid; 
    }]; 

    for (int i = 0; i < 5; i++) { 
     [self.queue addOperationWithBlock:^{ 
      [NSThread sleepForTimeInterval:3]; 
      NSLog(@"Finished operation."); 
     }]; 
    } 


    NSLog(@"Waiting until all operations are finished."); 

    [self.queue waitUntilAllOperationsAreFinished]; 

    [application endBackgroundTask:self.task]; 
    self.task = UIBackgroundTaskInvalid; 

    NSLog(@"All done :)"); 
} 

@end 

Tôi làm gì sai?

Cảm ơn

Trả lời

4

Mã mẫu của bạn không chặn vĩnh viễn tại bất kỳ điểm nào trên iOS 6.1 trên iPhone hoặc trình mô phỏng. Đặt một breakpoint tại dòng [application endBackgroundTask:self.task];, và bạn sẽ đánh nó mỗi lần.

Hành vi bạn đang thấy là vì bạn đang đăng nhập trong khi ở chế độ nền sau khi bạn đã yêu cầu ứng dụng kết thúc tác vụ nền. Tôi không chắc chắn về các chi tiết chính xác, nhưng các bản ghi được xếp hàng đợi bằng cách nào đó để được in lên bàn điều khiển khi khôi phục ứng dụng của bạn lên nền trước. Nó có thể là trường hợp thực hiện thread bị đình chỉ thay thế.

Nếu bạn di chuyển NSLog(@"All done"); tối đa trước cuộc gọi đến endBackgroundTask:, bạn sẽ thấy kết quả được ghi nhật ký.

+0

Ugh ... cảm ơn lol – chinabuffet

1

Sử dụng mã mẫu của bạn, các thao tác hoàn tất tốt. Đó là trên NSLog(@"All done :)"); mà bạn đang treo. Hàng đợi của bạn vẫn tồn tại và không có hoạt động đang chờ xử lý, nhưng bạn có thể không còn có một chuỗi hoạt động và chuỗi chính bị chặn như bạn và nền. Vì bạn đã hoàn tất các hoạt động nền đang chờ xử lý của mình, bạn sẽ bị chặn. Khi bạn tiếp tục ứng dụng của bạn tiếp tục nơi nó rời đi. Nếu bạn làm điều này:

[[self queue] addOperationWithBlock:^{ 
     NSLog(@"All done :)"); 
    }]; 

Hành vi này phải rõ ràng hơn. Nó làm chính xác những gì bạn đang nói nó làm.

Bạn có một loạt các hoạt động được xếp hàng đợi và bạn đã gọi là waitUntilAllOperationsAreFinished. Khi họ kết thúc, bạn bị chặn trong nền.

Dường như bạn đang cố gắng thực hiện điều gì đó khi nhóm hoạt động này kết thúc. NSOperation cung cấp khả năng có các khối phụ thuộc và khối hoàn thành cho phép bạn xây dựng loại hành vi này. Bạn có thể nhóm hoạt động và thiết lập một khối hoàn thành hoặc hoạt động chạy khi nhóm hoạt động của bạn hoàn thành. Một số điều này được bao gồm trong Concurrency Programming Guide

+0

Hmm ... Tôi đoán những gì tôi định làm là sử dụng 'waitUntilAllOperationsAreFinished' làm rào cản/cổng, và một lần qua cổng đó, thực hiện thao tác hoàn tất (trong trường hợp này, tải tất cả dữ liệu đã tải xuống vào Core Dữ liệu). Bạn có gợi ý rằng thay vào đó tôi tạo một hoạt động khác, điều đó phụ thuộc vào tất cả các hoạt động khác, thực hiện hành động kết thúc đó? Do đó ngăn chặn sự cần thiết phải sử dụng 'waitUntilAllOperationsAreFinished'? – chinabuffet

+0

Bạn có thể làm điều đó, bạn có thể thực hiện một thao tác có tất cả các hoạt động khác như phụ thuộc, bạn có thể thực hiện một thao tác có chứa các thao tác khác, v.v. hàng đợi thực hiện hành động hoàn thiện. Bạn vẫn muốn sử dụng '' waitUntilAllOperationsAreFinished'' để đảm bảo hàng đợi chạy trong khi bạn đang ở chế độ nền. Bạn chỉ cần có hành động hoàn thiện của bạn - bất kể nó là gì - trong hàng đợi trước khi bạn gọi nó. – quellish

+0

Tại sao 'waitUntilAllOperationsAreFinished' vẫn cần được sử dụng trong trường hợp đó nếu tác vụ nền đã được bắt đầu? – chinabuffet

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