2010-09-24 29 views
7

Nó xuất hiện sau nhiều lần tìm kiếm có vẻ là một vấn đề thường gặp khi cố gắng sao chép tệp và hiển thị chỉ báo tiến trình tương ứng với số lượng tệp đã được sao chép. Sau khi dành một số thời gian đáng kể để giải quyết vấn đề này, tôi thấy mình ở lòng thương xót của các vị thần StackOverflow một lần nữa :-) - Hy vọng rằng một ngày nào đó tôi sẽ là một trong những người có thể giúp đỡ những tân binh!Hiển thị tiến trình sao chép tập tin bằng cách sử dụng FSCopyObjectAsync

Tôi đang cố gắng lấy thanh tiến trình để hiển thị trạng thái của quá trình sao chép và sau khi quá trình sao chép hoàn tất, hãy gọi phương thức Cocoa. Thách thức - Tôi cần sử dụng các cuộc gọi Carbon của Trình quản lý tệp vì NSFileManager không cung cấp cho tôi khả năng đầy đủ mà tôi cần.

Tôi bắt đầu bằng cách cố gắng sử dụng mã trên trang web của Matt Long Cocoa Is My Girlfriend. Mã đã cho tôi một số khoảng cách tốt. Tôi quản lý để có được tiến trình sao chép tập tin làm việc. Các cập nhật thanh và (với một số tìm kiếm thêm trong tài liệu Apple) tôi phát hiện ra làm thế nào để nói nếu quá trình sao chép tập tin đã hoàn tất ...

if (stage == kFSOperationStageComplete)

Tuy nhiên, tôi có một trở ngại cuối cùng đó là lớn hơn một chút hơn là bước nhảy vọt của tôi ngay bây giờ. Tôi không biết làm thế nào để vượt qua một tham chiếu đối tượng vào gọi lại và tôi không biết làm thế nào để gọi một phương pháp Cocoa từ gọi lại sau khi hoàn thành. Đây là giới hạn của Carbon -> Ca cao -> Hiểu biết về Carbon của tôi. Một trong những nhận xét trên blog cho biết

"Thay vì truy cập chỉ báo tiến trình thông qua con trỏ tĩnh, bạn chỉ có thể sử dụng trường thông tin void * của cấu trúc FSFileOperationClientContext và chuyển AppDelegate hoặc chỉ báo tiến trình . "

Nghe như một ý tưởng tuyệt vời. Không chắc làm việc này như thế nào. Vì lợi ích của mọi người khác xuất hiện trong vấn đề này và xuất phát từ nền không phải Carbon, chủ yếu dựa trên mã từ ví dụ của Matt, dưới đây là một số mã đơn giản như một ví dụ về vấn đề ...

trong một phương pháp ca cao bình thường:

CFRunLoopRef runLoop = CFRunLoopGetCurrent(); 
FSFileOperationRef fileOp = FSFileOperationCreate(kCFAllocatorDefault); 

OSStatus status = FSFileOperationScheduleWithRunLoop(fileOp, 
        runLoop, kCFRunLoopDefaultMode); 

if (status) { 
    NSLog(@"Failed to schedule operation with run loop: %@", status); 
    return NO; 
} 

// Create a filesystem ref structure for the source and destination and 
// populate them with their respective paths from our NSTextFields. 

FSRef source; 
FSRef destination; 

// Used FSPathMakeRefWithOptions instead of FSPathMakeRef which is in the 
// original example because I needed to use the kFSPathMakeRefDefaultOptions 
// to deal with file paths to remote folders via a /Volume reference 

FSPathMakeRefWithOptions((const UInt8 *)[aSource fileSystemRepresentation], 
    kFSPathMakeRefDefaultOptions, 
    &source, 
    NULL); 

Boolean isDir = true; 

FSPathMakeRefWithOptions((const UInt8 *)[aDestDir fileSystemRepresentation], 
    kFSPathMakeRefDefaultOptions, 
    &destination, 
    &isDir); 

// Needed to change from the original to use CFStringRef so I could convert 
// from an NSString (aDestFile) to a CFStringRef (targetFilename) 

CFStringRef targetFilename = (CFStringRef)aDestFile; 

// Start the async copy. 

status = FSCopyObjectAsync (fileOp, 
      &source, 
      &destination, // Full path to destination dir 
      targetFilename, 
      kFSFileOperationDefaultOptions, 
      statusCallback, 
      1.0, 
      NULL); 

CFRelease(fileOp); 

if (status) { 

    NSString * errMsg = [NSString stringWithFormat:@"%@ - %@", 
          [self class], status]; 

     NSLog(@"Failed to begin asynchronous object copy: %@", status); 
} 

Sau đó gọi lại (trong cùng một tập tin)

static void statusCallback (FSFileOperationRef fileOp, 
      const FSRef *currentItem, 
      FSFileOperationStage stage, 
      OSStatus error, 
      CFDictionaryRef statusDictionary, 
      void *info) 
{ 

    NSLog(@"Callback got called."); 

    // If the status dictionary is valid, we can grab the current values to 
    // display status changes, or in our case to update the progress indicator. 

    if (statusDictionary) 
    { 

     CFNumberRef bytesCompleted; 

     bytesCompleted = (CFNumberRef) CFDictionaryGetValue(statusDictionary, 
       kFSOperationBytesCompleteKey); 

     CGFloat floatBytesCompleted; 
     CFNumberGetValue (bytesCompleted, kCFNumberMaxType, 
           &floatBytesCompleted); 

     NSLog(@"Copied %d bytes so far.", 
           (unsigned long long)floatBytesCompleted); 

     // fileProgressIndicator is currently declared as a pointer to a 
     // static progress bar - but this needs to change so that it is a 
     // pointer passed in via the controller. Would like to have a 
     // pointer to an instance of a progress bar 

     [fileProgressIndicator setDoubleValue:(double)floatBytesCompleted]; 
     [fileProgressIndicator displayIfNeeded]; 
    } 

if (stage == kFSOperationStageComplete) { 

    NSLog(@"Finished copying the file"); 

    // Would like to call a Cocoa Method here... 
} 

} 

Vì vậy, mấu chốt là như thế nào tôi có thể:

  1. Vượt qua một con trỏ tới một thể hiện của một thanh tiến từ phương pháp gọi điện thoại để gọi lại
  2. Sau khi hoàn thành, gọi lại ra một phương pháp Cocoa bình thường

Và như thường lệ, giúp đỡ được nhiều đánh giá (và hy vọng câu trả lời sẽ giải quyết nhiều vấn đề và khiếu nại mà tôi đã thấy trong nhiều chủ đề !!)

Trả lời

7

Bạn có thể làm điều này bằng cách sử dụng tham số cuối cùng là FSCopyObjectAsync(), là cấu trúc loại FSFileOperationClientContext. Một trong các trường của cấu trúc đó là info, là một tham số void * mà bạn về cơ bản có thể sử dụng khi bạn thấy phù hợp. Bất cứ điều gì bạn gán cho trường đó của cấu trúc bạn chuyển vào FSCopyObjectAsync() sẽ được chuyển đến hàm gọi lại của bạn làm tham số hàm info cuối cùng ở đó. Một void * có thể là bất cứ thứ gì, bao gồm một con trỏ tới một đối tượng, vì vậy bạn có thể sử dụng nó để truyền thể hiện của đối tượng mà bạn muốn xử lý cuộc gọi lại.

Mã cài đặt sẽ trông như thế này:

FSFileOperationClientContext clientContext = {0}; //zero out the struct to begin with 

clientContext.info = myProgressIndicator; 
//All the other setup code 
status = FSCopyObjectAsync (fileOp, 
     &source, 
     &destination, // Full path to destination dir 
     targetFilename, 
     kFSFileOperationDefaultOptions, 
     statusCallback, 
     1.0, 
     &clientContext); 

Sau đó, trong hàm callback của bạn:

static void statusCallback (FSFileOperationRef fileOp, 
     const FSRef *currentItem, 
     FSFileOperationStage stage, 
     OSStatus error, 
     CFDictionaryRef statusDictionary, 
     void *info) 
{ 
    NSProgressIndicator* fileProgressIndicator = (NSProgressIndicator*)info; 
    [fileProgressIndicator setDoubleValue:(double)floatBytesCompleted]; 
    [fileProgressIndicator displayIfNeeded]; 
} 
+1

Brian nhờ !!! Bây giờ tôi thấy nó, giải pháp có vẻ rất đơn giản! Tôi đã kết thúc việc khởi tạo một cá thể lớp để xử lý bảng tiến trình hoàn toàn và thông qua cá thể lớp đó vào trong clientContext. Tôi đã có thể cập nhật thanh tiến trình và thực hiện các cuộc gọi phương thức đối với lớp. Tôi sẽ đăng một giải pháp đầy đủ cho bất cứ ai đã không hoàn toàn theo ý kiến ​​cuối cùng của tôi khi tôi nhận được một chút thời gian. Cảm ơn Brian lần nữa! – Hooligancat

+0

@Hooligancat Bạn có thể đăng giải pháp đầy đủ của mình không? Tôi có cùng một vấn đề: Tôi muốn vượt qua dụ lớp vào clientContext nhưng tôi không biết làm thế nào. – FR6

+0

@ FR6 - Tôi đã không chạm vào mã này trong một thời gian, nhưng tôi sẽ đào sâu và xem tôi có thể tìm thấy nó cho bạn và đăng toàn bộ mã không. Hãy theo dõi ... – Hooligancat

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