ios 如何检测 iPhone/iPad 设备上的总可用/可用磁盘空间?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5712527/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 19:37:07  来源:igfitidea点击:

How to detect total available/free disk space on the iPhone/iPad device?

iphoneiosipadnsfilemanagerdiskspace

提问by Code.Warrior

I'm looking for a better way to detect available/free disk space on the iPhone/iPad device programmatically.
Currently I'm using the NSFileManager to detect the disk space. Following is the snippet of the code which does the job for me:

我正在寻找一种更好的方法来以编程方式检测 iPhone/iPad 设备上的可用/可用磁盘空间。
目前我正在使用 NSFileManager 来检测磁盘空间。以下是为我完成工作的代码片段:

-(unsigned)getFreeDiskspacePrivate {
NSDictionary *atDict = [[NSFileManager defaultManager] attributesOfFileSystemForPath:@"/" error:NULL];
unsigned freeSpace = [[atDict objectForKey:NSFileSystemFreeSize] unsignedIntValue];
NSLog(@"%s - Free Diskspace: %u bytes - %u MiB", __PRETTY_FUNCTION__, freeSpace, (freeSpace/1024)/1024);

return freeSpace;
}


Am I correct with the above snippet? or is there any better way to know total available/free disk space.
I've to detect total free disk space, since we've to prevent our application to perform sync in the low disk space scenario.


我对上面的片段是否正确?或者有没有更好的方法来了解可用/可用磁盘空间总量。
我必须检测总可用磁盘空间,因为我们必须阻止我们的应用程序在低磁盘空间情况下执行同步。

回答by Code.Warrior

UPDATE: Since a lot of time has passed after this answer and new methods/APIs have been added, please check the updated answers below for Swift etc; Since I've not used them myself, I can't vouch for them.

Original answer: I found the following solution working for me:

更新:由于在此答案和添加新方法/API 之后已经过了很长时间,请检查下面的 Swift 等更新答案;因为我自己没有使用过它们,所以我不能保证它们。

原始答案:我发现以下解决方案对我有用:

-(uint64_t)getFreeDiskspace {
    uint64_t totalSpace = 0;
    uint64_t totalFreeSpace = 0;
    NSError *error = nil;  
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
    NSDictionary *dictionary = [[NSFileManager defaultManager] attributesOfFileSystemForPath:[paths lastObject] error: &error];  

    if (dictionary) {  
        NSNumber *fileSystemSizeInBytes = [dictionary objectForKey: NSFileSystemSize];  
        NSNumber *freeFileSystemSizeInBytes = [dictionary objectForKey:NSFileSystemFreeSize];
        totalSpace = [fileSystemSizeInBytes unsignedLongLongValue];
        totalFreeSpace = [freeFileSystemSizeInBytes unsignedLongLongValue];
        NSLog(@"Memory Capacity of %llu MiB with %llu MiB Free memory available.", ((totalSpace/1024ll)/1024ll), ((totalFreeSpace/1024ll)/1024ll));
    } else {  
        NSLog(@"Error Obtaining System Memory Info: Domain = %@, Code = %ld", [error domain], (long)[error code]);
    }  

    return totalFreeSpace;
}

It returns me exactly the size that iTunes displays when device is connected to machine.

当设备连接到机器时,它会准确返回 iTunes 显示的大小。

回答by David H

Revised source using unsigned long long:

使用 unsigned long long 修改源代码:

- (uint64_t)freeDiskspace
{
    uint64_t totalSpace = 0;
    uint64_t totalFreeSpace = 0;

    __autoreleasing NSError *error = nil;  
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
    NSDictionary *dictionary = [[NSFileManager defaultManager] attributesOfFileSystemForPath:[paths lastObject] error: &error];  

    if (dictionary) {  
        NSNumber *fileSystemSizeInBytes = [dictionary objectForKey: NSFileSystemSize];  
        NSNumber *freeFileSystemSizeInBytes = [dictionary objectForKey:NSFileSystemFreeSize];
        totalSpace = [fileSystemSizeInBytes unsignedLongLongValue];
        totalFreeSpace = [freeFileSystemSizeInBytes unsignedLongLongValue];
        NSLog(@"Memory Capacity of %llu MiB with %llu MiB Free memory available.", ((totalSpace/1024ll)/1024ll), ((totalFreeSpace/1024ll)/1024ll));
    } else {  
        NSLog(@"Error Obtaining System Memory Info: Domain = %@, Code = %d", [error domain], [error code]);  
    }  

    return totalFreeSpace;
}

EDIT: it seems someone edited this code to use 'uint64_t' instead of 'unsigned long long'. While in the foreseeable future this should be just fine, they are not the same. 'uint64_t' is 64 bits and will always be that. In 10 years 'unsigned long long' might be 128. its a small point but why I used unsignedLongLong.

编辑:似乎有人编辑了此代码以使用 'uint64_t' 而不是 'unsigned long long'。虽然在可预见的未来这应该没问题,但它们并不相同。'uint64_t' 是 64 位,并且永远都是。10 年后,'unsigned long long' 可能是 128。这是一个小点,但为什么我使用 unsignedLongLong。

回答by Cuong Lam

I have written a class to get available/used memory using Swift. Demo at: https://github.com/thanhcuong1990/swift-disk-status
Swift 4 updated.

我编写了一个类来使用 Swift 获取可用/已用内存。演示在:https: //github.com/thanhcuong1990/swift-disk-status
Swift 4 更新。

import UIKit

class DiskStatus {

    //MARK: Formatter MB only
    class func MBFormatter(_ bytes: Int64) -> String {
        let formatter = ByteCountFormatter()
        formatter.allowedUnits = ByteCountFormatter.Units.useMB
        formatter.countStyle = ByteCountFormatter.CountStyle.decimal
        formatter.includesUnit = false
        return formatter.string(fromByteCount: bytes) as String
    }


    //MARK: Get String Value
    class var totalDiskSpace:String {
        get {
            return ByteCountFormatter.string(fromByteCount: totalDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.file)
        }
    }

    class var freeDiskSpace:String {
        get {
            return ByteCountFormatter.string(fromByteCount: freeDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.file)
        }
    }

    class var usedDiskSpace:String {
        get {
            return ByteCountFormatter.string(fromByteCount: usedDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.file)
        }
    }


    //MARK: Get raw value
    class var totalDiskSpaceInBytes:Int64 {
        get {
            do {
                let systemAttributes = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String)
                let space = (systemAttributes[FileAttributeKey.systemSize] as? NSNumber)?.int64Value
                return space!
            } catch {
                return 0
            }
        }
    }

    class var freeDiskSpaceInBytes:Int64 {
        get {
            do {
                let systemAttributes = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String)
                let freeSpace = (systemAttributes[FileAttributeKey.systemFreeSize] as? NSNumber)?.int64Value
                return freeSpace!
            } catch {
                return 0
            }
        }
    }

    class var usedDiskSpaceInBytes:Int64 {
        get {
            let usedSpace = totalDiskSpaceInBytes - freeDiskSpaceInBytes
            return usedSpace
        }
    }

}

Demo

演示

get disk space status with Swift

使用 Swift 获取磁盘空间状态

回答by 0x8BADF00D

If you need formatted string with size you can take a look at nice library on GitHub:

如果您需要带大小的格式化字符串,您可以在 GitHub 上查看漂亮的库

#define MB (1024*1024)
#define GB (MB*1024)

@implementation ALDisk

#pragma mark - Formatter

+ (NSString *)memoryFormatter:(long long)diskSpace {
    NSString *formatted;
    double bytes = 1.0 * diskSpace;
    double megabytes = bytes / MB;
    double gigabytes = bytes / GB;
    if (gigabytes >= 1.0)
        formatted = [NSString stringWithFormat:@"%.2f GB", gigabytes];
    else if (megabytes >= 1.0)
        formatted = [NSString stringWithFormat:@"%.2f MB", megabytes];
    else
        formatted = [NSString stringWithFormat:@"%.2f bytes", bytes];

    return formatted;
}

#pragma mark - Methods

+ (NSString *)totalDiskSpace {
    long long space = [[[[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil] objectForKey:NSFileSystemSize] longLongValue];
    return [self memoryFormatter:space];
}

+ (NSString *)freeDiskSpace {
    long long freeSpace = [[[[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil] objectForKey:NSFileSystemFreeSize] longLongValue];
    return [self memoryFormatter:freeSpace];
}

+ (NSString *)usedDiskSpace {
    return [self memoryFormatter:[self usedDiskSpaceInBytes]];
}

+ (CGFloat)totalDiskSpaceInBytes {
    long long space = [[[[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil] objectForKey:NSFileSystemSize] longLongValue];
    return space;
}

+ (CGFloat)freeDiskSpaceInBytes {
    long long freeSpace = [[[[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil] objectForKey:NSFileSystemFreeSize] longLongValue];
    return freeSpace;
}

+ (CGFloat)usedDiskSpaceInBytes {
    long long usedSpace = [self totalDiskSpaceInBytes] - [self freeDiskSpaceInBytes];
    return usedSpace;
}

回答by Christopher Lloyd

Don't use 'unsigned', it is only 32 bits which will overflow past 4GB, which is less than the typical iPad/iPhone free space. Use unsigned long long (or uint64_t), and retrieve the value out of the NSNumber as a 64-bit int too using unsignedLongLongValue.

不要使用“无符号”,它只有 32 位会溢出超过 4GB,这比典型的 iPad/iPhone 可用空间要少。使用 unsigned long long(或 uint64_t),并使用 unsignedLongLongValue 从 NSNumber 中检索值作为 64 位 int。

回答by RubenSandwich

If your looking to get the the remaining free space using Swift it is slightly different. You need to use attributesOfFileSystemForPath() instead of attributesOfItemAtPath():

如果您希望使用 Swift 获得剩余的可用空间,则略有不同。您需要使用 attributesOfFileSystemForPath() 而不是 attributesOfItemAtPath():

func deviceRemainingFreeSpaceInBytes() -> Int64? {
    let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
    var attributes: [String: AnyObject]
    do {
        attributes = try NSFileManager.defaultManager().attributesOfFileSystemForPath(documentDirectoryPath.last! as String)
        let freeSize = attributes[NSFileSystemFreeSize] as? NSNumber
        if (freeSize != nil) {
            return freeSize?.longLongValue
        } else {
            return nil
        }
    } catch {
        return nil
    }
}

Edit: Updated for Swift 1.0
Edit 2: Updated for safety, using Martin R's answer.
Edit 3: Updated for Swift 2.0 (by dgellow)

编辑:针对 Swift 1.0 进行了更新
编辑 2:为安全起见进行了更新,使用 Martin R 的回答
编辑 3:针对 Swift 2.0 更新(由dgellow 提供

回答by ChrisJF

Here's my answer and why it's better.

这是我的答案以及为什么它更好。

Answer (Swift):

答案(斯威夫特):

func remainingDiskSpaceOnThisDevice() -> String {
    var remainingSpace = NSLocalizedString("Unknown", comment: "The remaining free disk space on this device is unknown.")
    if let attributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory()),
        let freeSpaceSize = attributes[FileAttributeKey.systemFreeSize] as? Int64 {
        remainingSpace = ByteCountFormatter.string(fromByteCount: freeSpaceSize, countStyle: .file)
    }
    return remainingSpace
}

Answer (Objective-C):

答案(目标-C):

- (NSString *)calculateRemainingDiskSpaceOnThisDevice
{
    NSString *remainingSpace = NSLocalizedString(@"Unknown", @"The remaining free disk space on this device is unknown.");
    NSDictionary *dictionary = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil];
    if (dictionary) {
        long long freeSpaceSize = [[dictionary objectForKey:NSFileSystemFreeSize] longLongValue];
        remainingSpace = [NSByteCountFormatter stringFromByteCount:freeSpaceSize countStyle:NSByteCountFormatterCountStyleFile];
    }
    return remainingSpace;
}

Why it's better:

为什么更好:

  • Utilizes Cocoa's built in library NSByteCountFormatter, meaning no crazy manual calculations from bytes to gigabytes. Apple does this for you!
  • Easily translatable: NSByteCountFormatterdoes this for you. E.g. When the device's language is set to English the string will read 248.8 MB but will read 248,8 Mo when set to French, et cetera for other languages.
  • A default value is given in case of an error.
  • 利用 Cocoa 的内置库NSByteCountFormatter,这意味着没有从字节到千兆字节的疯狂手动计算。苹果为你做到了!
  • 易于翻译:NSByteCountFormatter为您做这件事。例如,当设备的语言设置为英语时,字符串将读取 248.8 MB,但在设置为法语时将读取 248,8 Mo,其他语言等。
  • 出现错误时会给出默认值。

回答by Markus

Important clarification (at least for me). If I connect my iPod to my Mac this is the info showed by iTunes App.

重要的澄清(至少对我而言)。如果我将 iPod 连接到 Mac,这是 iTunes 应用程序显示的信息。

iPod memory informations from iTunes app

来自 iTunes 应用程序的 iPod 内存信息

When I use the above code:

当我使用上面的代码时:

long long freeSpace = [[[[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil]
                            objectForKey:NSFileSystemFreeSize] longLongValue];

NSString *free1 = [NSByteCountFormatter stringFromByteCount:freeSpace countStyle:NSByteCountFormatterCountStyleFile];

[label1 setText:free1];

NSString *free2 = [NSByteCountFormatter stringFromByteCount:freeSpace countStyle:NSByteCountFormatterCountStyleBinary];

[label2 setText:free2];

The countStyle NSByteCountFormatterCountStyleFileshow me: 17,41 GB

countStyle NSByteCountFormatterCountStyleFile显示我:17,41 GB

The countStyle NSByteCountFormatterCountStyleBinaryshow me: 16,22 GB

countStyle NSByteCountFormatterCountStyleBinary显示我:16,22 GB

16,22 GB(NSByteCountFormatterCountStyleBinary) It is EXACTLYthe number that iTunes App show me when I connect my iPod to my Mac.

16,22 GBNSByteCountFormatterCountStyleBinary)是EXACTLY是iTunes应用程序告诉我,当我在我的iPod连接到我的Mac数量。

回答by Evilisn Jiang

Update with a new accurate API to get available size on disk available in iOS11. Here is the description for the new API resource key:

使用新的准确 API 进行更新,以获取 iOS11 中可用磁盘的可用大小。以下是新 API 资源密钥的说明:

#if os(OSX) || os(iOS)
/// Total available capacity in bytes for "Important" resources, including space expected to be cleared by purging non-essential and cached resources. "Important" means something that the user or application clearly expects to be present on the local system, but is ultimately replaceable. This would include items that the user has explicitly requested via the UI, and resources that an application requires in order to provide functionality.
/// Examples: A video that the user has explicitly requested to watch but has not yet finished watching or an audio file that the user has requested to download.
/// This value should not be used in determining if there is room for an irreplaceable resource. In the case of irreplaceable resources, always attempt to save the resource regardless of available capacity and handle failure as gracefully as possible.
@available(OSX 10.13, iOS 11.0, *) @available(tvOS, unavailable) @available(watchOS, unavailable)
public var volumeAvailableCapacityFor Usage: Int64? { return _get(.volumeAvailableCapacityForImportantUsageKey) }
#endif

I cross compared the results from key "FileAttributeKey.systemFreeSize" and key "URLResourceKey.volumeAvailableCapacityForImportantUsageKey" and found the results returned form "volumeAvailableCapacityForImportantUsageKey" exactly matches the available storage shown on UI. Available free disk space compareHere is the swift implementation:

我交叉比较了键“ FileAttributeKey.systemFreeSize”和键“ URLResourceKey.volumeAvailableCapacityForImportantUsageKey”的结果,发现从“ volumeAvailableCapacityForImportantUsageKey”返回的结果与UI上显示的可用存储完全匹配。 可用磁盘空间比较这是快速实现:

class var freeDiskSpaceInBytesImportant:Int64 {
    get {
        do {
            return try URL(fileURLWithPath: NSHomeDirectory() as String).resourceValues(forKeys: [URLResourceKey.volumeAvailableCapacityForImportantUsageKey]).volumeAvailableCapacityForImportantUsage!
        } catch {
            return 0
        }
    }
}

回答by abdullahselek

You can find an another solution with using Swift 4and extensionwhich gives you a good option.

你可以找到与使用另一种解决方案斯威夫特4extension它给你一个不错的选择。

Here is the UIDeviceextension.

这是UIDevice扩展名。

extension UIDevice {

    func totalDiskSpaceInBytes() -> Int64 {
        do {
            guard let totalDiskSpaceInBytes = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory())[FileAttributeKey.systemSize] as? Int64 else {
                return 0
            }
            return totalDiskSpaceInBytes
        } catch {
            return 0
        }
    }

    func freeDiskSpaceInBytes() -> Int64 {
        do {
            guard let totalDiskSpaceInBytes = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory())[FileAttributeKey.systemFreeSize] as? Int64 else {
                return 0 
            }
            return totalDiskSpaceInBytes
        } catch {
            return 0
        }
    }

    func usedDiskSpaceInBytes() -> Int64 {
        return totalDiskSpaceInBytes() - freeDiskSpaceInBytes()
    }

    func totalDiskSpace() -> String {
        let diskSpaceInBytes = totalDiskSpaceInBytes()
        if diskSpaceInBytes > 0 {
            return ByteCountFormatter.string(fromByteCount: diskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.binary)
        }
        return "The total disk space on this device is unknown"
    }

    func freeDiskSpace() -> String {
        let freeSpaceInBytes = freeDiskSpaceInBytes()
        if freeSpaceInBytes > 0 {
            return ByteCountFormatter.string(fromByteCount: freeSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.binary)
        }
        return "The free disk space on this device is unknown"
    }

    func usedDiskSpace() -> String {
        let usedSpaceInBytes = totalDiskSpaceInBytes() - freeDiskSpaceInBytes()
        if usedSpaceInBytes > 0 {
            return ByteCountFormatter.string(fromByteCount: usedSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.binary)
        }
        return "The used disk space on this device is unknown"
    }

}

And sample usage:

和示例用法:

UIDevice.current.totalDiskSpaceInBytes()
UIDevice.current.totalDiskSpace()
UIDevice.current.freeDiskSpaceInBytes()
UIDevice.current.freeDiskSpace()
UIDevice.current.usedDiskSpaceInBytes()
UIDevice.current.usedDiskSpace()