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

Cod*_*ior 141 iphone diskspace nsfilemanager ipad ios

我正在寻找一种更好的方法来以编程方式检测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;
}
Run Code Online (Sandbox Code Playgroud)


我是否正确使用上述代码段?或者有更好的方法来了解总可用/可用磁盘空间.
我要检测总可用磁盘空间,因为我们要阻止我们的应用程序在低磁盘空间场景中执行同步.

Cod*_*ior 151

更新:由于在此答案和新方法/ 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;
}
Run Code Online (Sandbox Code Playgroud)

它返回的确切地说是设备连接到机器时iTunes显示的大小.

  • 使用NSLog格式时,`[错误代码]`不应该使用`%@`. (6认同)
  • 转换为浮点数可能会导致大约2GB以上的不准确结果.如果您需要处理非常大的文件大小,请使用double或long double. (4认同)
  • +200 MB不是问题.在设置中我有"0字节"可用空间.当我输入并使用我的应用程序时,此方法报告大约150mb的可用空间.然后我填满剩余的空间,然后应用程序崩溃.所以我想说这种方法比你在"设置"中看到的信息更准确. (4认同)
  • 为什么没有人使用`NSUInteger`而不是像`uint64_t`这样的东西?我们正在编写Obj-C,而不是C++或C. NSUInteger现在会给你一个无符号的64位整数,但如果事情发生变化,我想Apple会更新那个宏(比如说128位在某个时候变成真实的) (4认同)
  • 代码段已经过编辑,以反映@David H的建议,如下所示. (3认同)

Dav*_*d H 58

使用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;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 在运行iOS 5.1的iPod Touch第4代上,NSFileSystemFreeSize仍然报告~200 MBytes太多.我在调试器中打印出整个NSDictionary的内容......虽然NSFileSystemSize是正确的......任何人都有解决这个问题的方法吗? (3认同)

0x8*_*00D 26

如果你需要格式化的字符串大小,你可以看看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;
}
Run Code Online (Sandbox Code Playgroud)

  • 要格式化,还可以使用[NSBytCounterFormatter](https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSByteCountFormatter_Class/index.html) (2认同)

Cuo*_*Lam 25

我已经编写了一个类来使用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
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

演示

使用Swift获取磁盘空间状态

  • @CuongLam哇,哪个iOs设备出货200 gb? (2认同)
  • iTunes 显示 18.99 GB 免费,但当我使用描述的方法时,我得到 13.41 GB。有谁知道我想念什么? (2认同)
  • @CuongLam 展开错误不会抛出,也不会被 do/catch 捕获。应编写示例源代码以正确处理错误。/sf/ask/2424029961/ (2认同)

Chr*_*oyd 13

不要使用'unsigned',它只有32位,它将超过4GB,这比典型的iPad/iPhone可用空间要少.使用unsigned long long(或uint64_t),并使用unsignedLongLongValue将值从NSNumber中检索为64位int.

  • 它比小费更好 - "它的法律":-)正如他所说,原始代码是完全错误的. (3认同)

Rub*_*ich 12

如果您希望使用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
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:已更新为Swift 1.0
编辑2:使用Martin R的答案更新了安全性.
编辑3:更新了Swift 2.0(由dgellow提供)


Evi*_*ang 11

使用新的准确 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
Run Code Online (Sandbox Code Playgroud)

我交叉比较了键“ 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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Chr*_*sJF 9

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

答案(斯威夫特):

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
}
Run Code Online (Sandbox Code Playgroud)

答案(目标-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;
}
Run Code Online (Sandbox Code Playgroud)

为什么它更好:

  • 利用Cocoa的内置库NSByteCountFormatter,意味着没有从字节到千兆字节的疯狂手动计算.Apple为您做到了这一点!
  • 轻松翻译:NSByteCountFormatter为您做到这一点.例如,当设备的语言设置为英语时,字符串将读取248.8 MB,但设置为法语时将读取248,8 Mo,等等其他语言.
  • 出现错误时会给出默认值.


abd*_*lek 8

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

这是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"
    }

}
Run Code Online (Sandbox Code Playgroud)

和示例用法:

UIDevice.current.totalDiskSpaceInBytes()
UIDevice.current.totalDiskSpace()
UIDevice.current.freeDiskSpaceInBytes()
UIDevice.current.freeDiskSpace()
UIDevice.current.usedDiskSpaceInBytes()
UIDevice.current.usedDiskSpace()
Run Code Online (Sandbox Code Playgroud)


Mar*_*kus 7

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

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

当我使用上面的代码时:

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];
Run Code Online (Sandbox Code Playgroud)

countStyle NSByteCountFormatterCountStyleFile告诉我:17,41 GB

countStyle NSByteCountFormatterCountStyleBinary告诉我:16,22 GB

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


iva*_*oid 6

这是 Swift 5 扩展,FileManager具有正确的错误处理功能,并且没有自动字符串转换(根据您的喜好将字节数转换为字符串)。也遵循FileManager的命名。

extension FileManager {
    func systemFreeSizeBytes() -> Result<Int64, Error> {
        do {
            let attrs = try attributesOfFileSystem(forPath: NSHomeDirectory())
            guard let freeSize = attrs[.systemFreeSize] as? Int64 else {
                return .failure(NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey : "Can't retrieve system free size"]))
            }
            return .success(freeSize)
        } catch {
            return .failure(error)
        }
    }

    func systemSizeBytes() -> Result<Int64, Error> {
         do {
             let attrs = try attributesOfFileSystem(forPath: NSHomeDirectory())
             guard let size = attrs[.systemSize] as? Int64 else {
                 return .failure(NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey : "Can't retrieve system size"]))
             }
             return .success(size)
         } catch {
             return .failure(error)
         }
     }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

let freeSizeResult = FileManager.default.systemFreeSizeBytes()
switch freeSizeResult {
case .failure(let error):
    print(error)
case .success(let freeSize):
    let freeSizeString = ByteCountFormatter.string(fromByteCount: freeSize, countStyle: .file)
    print("free size: \(freeSizeString)")
}
Run Code Online (Sandbox Code Playgroud)