【问题标题】:how to determine how much time needed when calling moveItemAtURL:toURL: or replaceItemAtURL:WithItemAtURL:如何确定调用 moveItemAtURL:toURL: 或 replaceItemAtURL:WithItemAtURL: 时需要多少时间
【发布时间】:2013-04-28 03:58:21
【问题描述】:

当将文件从一个地方移动到另一个地方,或者替换文件时,我总是使用NSFileManager 中的moveItemAtURL:toURL:replaceItemAtURL:WithItemAtURL: 方法。

当调用这些方法时,我想确定需要多少时间,以便我可以使用NSProgressIndicator 告诉用户需要多长时间。就像您使用 OSX 移动文件一样,它会告诉您剩余的时间。

我查看了苹果文档,但找不到任何相关信息。 想知道这是否可以实现,请指教。

【问题讨论】:

    标签: iphone objective-c xcode cocoa nsfilemanager


    【解决方案1】:

    您无法提前知道需要多长时间。您可以做的是在复制文件时计算“完成百分比”。但要做到这一点,您需要使用较低级别的 API。您可以使用 NSFileManagers attributesOfItemAtPath:error 获取文件大小和 NSStreams 进行复制(有很多方法可以做到这一点)。完成百分比是bytesWritten / totalBytesInFile

    --- 编辑:添加示例代码作为 NSURL 上的一个类别,其中一个回调块传递写入的字节总数、完成百分比和估计剩余时间(以秒为单位)。

    #import <mach/mach_time.h>
    
    @interface NSURL(CopyWithProgress)<NSObject>
    - (void) copyFileURLToURL:(NSURL*)destURL withProgressBlock:(void(^)(double, double, double))block;
    @end
    
    @implementation NSURL(CopyWithProgress)
    
    - (void) copyFileURLToURL:(NSURL*)destURL
            withProgressBlock:(void(^)(double, double, double))block
    {
        ///
        // NOTE: error handling has been left out in favor of simplicity
        //       real production code should obviously handle errors.
        NSUInteger fileSize = [[NSFileManager defaultManager] attributesOfItemAtPath:self.path error:nil].fileSize;
    
        NSInputStream  *fileInput  = [NSInputStream inputStreamWithURL:self];
        NSOutputStream *copyOutput = [NSOutputStream outputStreamWithURL:destURL append:NO];
    
        static size_t bufferSize = 4096;
        uint8_t *buffer = malloc(bufferSize);
        size_t   bytesToWrite;
        size_t   bytesWritten;
        size_t   copySize = 0;
        size_t   counter  = 0;
    
        [fileInput open];
        [copyOutput open];
    
        uint64_t time0 = mach_absolute_time();
    
        while (fileInput.hasBytesAvailable) {
            do {
                bytesToWrite = [fileInput read:buffer maxLength:bufferSize];
                bytesWritten = [copyOutput write:buffer maxLength:bytesToWrite];
                bytesToWrite -= bytesWritten;
                copySize     += bytesWritten;
                if (bytesToWrite > 0)
                    memmove(buffer, buffer + bytesWritten, bytesToWrite);
            }
            while (bytesToWrite > 0);
    
            if (block != nil && ++counter % 10 == 0) {
                double percent  = (double)copySize / fileSize;
                uint64_t time1  = mach_absolute_time();
                double elapsed  = (double)(time1 - time0)/NSEC_PER_SEC;
                double estTimeLeft = ((1 - percent) / percent) * elapsed;
                block(copySize, percent, estTimeLeft);
            }
        }
    
        if (block != nil)
            block(copySize, 1, 0);
    }
    
    @end
    
    
    int main (int argc, const char * argv[])
    {
        @autoreleasepool {
    
            NSURL *fileURL = [NSURL URLWithString:@"file:///Users/eric/bin/data/english-words.txt"];
            NSURL *destURL = [NSURL URLWithString:@"file:///Users/eric/Desktop/english-words.txt"];
    
            [fileURL copyFileURLToURL:destURL withProgressBlock:^(double bytes, double pct, double estSecs) {
                NSLog(@"Bytes=%f, Pct=%f, time left:%f s",bytes,pct,estSecs);
            }];
        }
        return 0;
    }
    

    样本输出:

    Bytes=40960.000000, Pct=0.183890, time left:0.000753 s
    Bytes=81920.000000, Pct=0.367780, time left:0.004336 s
    Bytes=122880.000000, Pct=0.551670, time left:0.002672 s
    Bytes=163840.000000, Pct=0.735560, time left:0.001396 s
    Bytes=204800.000000, Pct=0.919449, time left:0.000391 s
    Bytes=222742.000000, Pct=1.000000, time left:0.000000 s
    

    【讨论】:

    • 找出 pct 完成也很好。 @aLevelOfIndirection 您能否提供一些示例代码(或请包含示例代码的链接)来说明如何做到这一点。谢谢
    【解决方案2】:

    我主要同意 CRD。我只想指出,在某些常见情况下,-moveItemAtURL:toURL:-replaceItemAtURL:WithItemAtURL:... 都非常快。当源和目标位于同一卷上时,无需复制或移动数据,只需复制或移动元数据。当卷是本地的(而不是网络安装的)时,这通常花费的时间可以忽略不计。也就是说,为他们可能需要大量时间进行计划是适当的。

    此外,他还提到了用于移动文件的copyfile() 例程。在卷之间移动文件时,复制然后删除原始文件是必要的方法,但rename() 系统调用将在卷内执行移动,而无需复制任何内容。因此,一个合理的方法是先尝试rename(),如果它以EXDEV 失败,则回退到copyfile()

    最后,exchangedata() 系统调用可以用作-replaceItemAtURL:WithItemAtURL:... 重新实现的一部分。

    我不推荐 aLevelOfIndirection 建议的方法,因为复制文件有很多繁琐的细节。依赖系统库比尝试使用自己的库要好得多。例如,他的示例完全忽略了文件元数据(文件日期、扩展属性等)。

    【讨论】:

    • 任何演示使用 copyfile、rename(0 和 exchangedata() 的示例代码?因为我对 c 不太熟悉。感谢任何帮助。
    【解决方案3】:

    moveItemAtURL:toURL:replaceItemAtURL:WithItemAtURL: 方法是高级操作。虽然它们提供了移动/替换所需的语义,但正如您所发现的,它们并没有在这些操作期间提供您希望的那种反馈。

    Apple 正在更改较低级别的文件处理例程,许多现在在 10.8 中被标记为已弃用,因此您需要仔细选择您选择使用的内容。但是,在最低级别,系统调用(手册第 2 节)和库函数(手册第 3 节),您可以使用的函数并未被弃用。

    一个选项,还有其他选项,是函数copyfile(手册第 3 节),它将复制文件或文件夹层次结构并提供进度回调。这应该为您提供moveItemAtURL:toURL: 的大部分语义以及进度,但您需要为replaceItemAtURL:WithItemAtURL: 做更多工作以保持安全(在发生错误时不会丢失数据)。

    如果这不能满足您的所有需求,您还可以另外查看低级stat 和朋友以了解文件大小等。

    HTH

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-25
      • 1970-01-01
      • 1970-01-01
      • 2021-08-08
      • 1970-01-01
      • 1970-01-01
      • 2016-03-22
      相关资源
      最近更新 更多