2010-03-24 39 views
5

Tôi có một vòng lặp while, chạy trong nhiều giây và đó là lý do tại sao tôi muốn cập nhật thanh tiến trình (NSProgressIndicator) trong quá trình đó, nhưng nó chỉ cập nhật một lần sau khi vòng lặp kết thúc. Điều tương tự cũng xảy ra nếu tôi muốn cập nhật văn bản nhãn, nhân tiện.Làm cách nào để cập nhật thanh tiến trình trong Cocoa trong vòng lặp chạy dài?

Tôi tin rằng, vòng lặp của tôi ngăn các ứng dụng khác xảy ra. Phải có một kỹ thuật khác. Điều này có liên quan đến chủ đề hay gì đó không? Có phải tôi đang trên đường ray bên phải không? Ai đó có thể xin vui lòng cho tôi một ví dụ đơn giản, làm thế nào để "tối ưu" ứng dụng của tôi?

ứng dụng của tôi là một ứng dụng Cocoa (Xcode 3.2.1) với hai phương pháp này trong Example_AppDelegate.m tôi:

 
// This method runs when a start button is clicked. 
- (IBAction)startIt:(id)sender { 
    [progressbar setDoubleValue:0.0]; 
    [progressbar startAnimation:sender]; 
    running = YES; // this is a instance variable 

    int i = 0; 
    while (running) { 
     if (i++ >= processAmount) { // processAmount is something like 1000000 
      running = NO; 
      continue; 
     } 

     // Update progress bar 
     double progr = (double)i/(double)processAmount; 
     NSLog(@"progr: %f", progr); // Logs values between 0.0 and 1.0 
     [progressbar setDoubleValue:progr]; 
     [progressbar needsDisplay]; // Do I need this? 

     // Do some more hard work here... 
    } 
} 

// This method runs when a stop button is clicked, but as long 
// as -startIt is busy, a click on the stop button does nothing. 
- (IBAction)stopIt:(id)sender { 
    NSLog(@"Stop it!"); 
    running = NO; 
    [progressbar stopAnimation:sender]; 
} 

Tôi thực sự mới để Objective-C, Cocoa và các ứng dụng với một giao diện người dùng. Cảm ơn bạn rất nhiều vì bất kỳ câu trả lời hữu ích nào.

Trả lời

15

Nếu bạn đang xây dựng cho Snow Leopard, giải pháp đơn giản nhất là theo ý kiến ​​của tôi để sử dụng khối và Grand Central Dispatch.

Mã sau đây cho bạn biết phương thức startIt: của bạn sẽ trông như thế nào khi sử dụng GCD.

Phương thức stopIt: của bạn sẽ hoạt động tốt như bạn đã viết. Lý do tại sao nó không được làm việc trước đây là các sự kiện chuột xảy ra trên các chủ đề chính và do đó các nút đã không đáp ứng với bạn bởi vì bạn đã làm việc trên các chủ đề chính. Vấn đề này nên được giải quyết ngay bây giờ vì công việc đã được đặt trên một chủ đề khác ngay bây giờ với GCD. Hãy thử mã, và nếu nó không hoạt động, hãy cho tôi biết và tôi sẽ xem nếu tôi đã thực hiện một số lỗi trong đó.

// This method runs when a start button is clicked. 
- (IBAction)startIt:(id)sender { 

    //Create the block that we wish to run on a different thread. 
    void (^progressBlock)(void); 
    progressBlock = ^{ 

    [progressbar setDoubleValue:0.0]; 
    [progressbar startAnimation:sender]; 
    running = YES; // this is a instance variable 

    int i = 0; 
    while (running) { 
     if (i++ >= processAmount) { // processAmount is something like 1000000 
      running = NO; 
      continue; 
     } 

     // Update progress bar 
     double progr = (double)i/(double)processAmount; 
     NSLog(@"progr: %f", progr); // Logs values between 0.0 and 1.0 

     //NOTE: It is important to let all UI updates occur on the main thread, 
     //so we put the following UI updates on the main queue. 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      [progressbar setDoubleValue:progr]; 
      [progressbar setNeedsDisplay:YES]; 
     }); 

     // Do some more hard work here... 
    } 

    }; //end of progressBlock 

    //Finally, run the block on a different thread. 
    dispatch_queue_t queue = dispatch_get_global_queue(0,0); 
    dispatch_async(queue,progressBlock); 
} 
+0

Cảm ơn rất nhiều, tôi sẽ cung cấp thử một ngày cuối tuần này! – Nick

+0

Đó là những gì tôi đang tìm kiếm.Hoạt động tốt và điều này mang lại cho tôi một điểm khởi đầu tốt để tìm hiểu thêm về nó. Cảm ơn! – Nick

+0

Cảm ơn Enchilada này! Tôi vừa học được điều mà tôi không biết! Rất vui mừng khi tìm hiểu về các khối và GCD !! –

2

Tôi tin rằng, vòng lặp của tôi ngăn những thứ khác của ứng dụng đó xảy ra.

Đúng. Bạn cần phải phá vỡ điều này bằng cách nào đó.

Một cách sẽ là bộ hẹn giờ, khi bạn bỏ qua hàng đợi một chút tại thời điểm gọi lại bộ hẹn giờ. Một cách khác là bọc mã để xử lý một mục trong lớp con NSOperation và tạo các cá thể của lớp đó (các hoạt động) và đặt chúng vào một NSOperationQueue.

Điều này có liên quan đến chủ đề hay gì đó không?

Không nhất thiết. NSOperations chạy trên các luồng, nhưng NSOperationQueue sẽ xử lý việc tạo luồng cho bạn. Bộ hẹn giờ là giải pháp một luồng: Mỗi bộ hẹn giờ chạy trên chuỗi bạn lên lịch. Đó có thể là một lợi thế hoặc bất lợi - bạn quyết định.

Xem the threads section of my intro to Cocoa để biết thêm chi tiết.

3

Bạn có thể thử mã này ..

[progressbar setUsesThreadedAnimation:YES]; 
2

này đã làm việc cho tôi, đó là sự kết hợp các câu trả lời từ những người khác mà dường như không làm việc (đối với tôi ít nhất) trên riêng của họ:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ 
    //do something 
    dispatch_async(dispatch_get_main_queue(), ^{ 
    progressBar.progress = (double)x/(double)[stockList count];    
    }); 
    //do something else 
}); 
Các vấn đề liên quan