我正在尝试使用Swift
获取可用的iOS设备存储空间。我找到了这个函数here
func deviceRemainingFreeSpaceInBytes() -> NSNumber {
let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let systemAttributes = NSFileManager.defaultManager().attributesOfFileSystemForPath(documentDirectoryPath.last as String, error: nil)
return systemAttributes[NSFileSystemFreeSize] as NSNumber
}
但是在编译时出现了这个错误:[NSObject : AnyObject]? does not have a member named 'subscript'
我认为这个错误来自于here提到的问题,即attributesOfFileSystemForPath
返回一个可选字典(documentation) 。我在一般意义上理解这个问题,但是由于建议的解决方案涉及嵌套的情况,我不太清楚如何修复我感兴趣的函数(这对我{{{{{{ 1}})。有人可以建议如何使功能工作?注意:我不确定作者是否测试了原始功能,或者它是否在xcode 6 beta下运行,但据我所知,它在GM下无效。
答案 0 :(得分:42)
下面给出的答案不再能够在iOS 11下提供准确的结果。新的音量容量键可以传递给URL.resourceValues(forKeys:)
,提供的值与设备设置中的值相匹配。
static let volumeAvailableCapacityKey: URLResourceKey
卷的可用容量的密钥(以字节为单位)(只读)。
static let volumeAvailableCapacityForImportantUsageKey: URLResourceKey
用于存储重要资源的卷的可用容量(以字节为单位)的密钥(只读)。
static let volumeAvailableCapacityForOpportunisticUsageKey: URLResourceKey
用于存储非必要资源的卷的可用容量(以字节为单位)的密钥(只读)。
static let volumeTotalCapacityKey: URLResourceKey
卷的总容量的密钥(以字节为单位)(只读)。
概述
在尝试在本地存储大量数据之前,请先验证您是否有足够的存储容量。要获取卷的存储容量,请构造一个URL(使用URL实例),该URL引用要查询的卷上的对象,然后查询该卷。
决定使用哪种查询类型
要使用的查询类型取决于要存储的内容。如果您根据应用程序需要正常运行的用户请求或资源来存储数据(例如,用户即将观看的视频或游戏中下一级所需的资源),请查询{{1 }}。但是,如果您以更具预测性的方式下载数据(例如,下载用户最近一直在观看的电视剧的新可用剧集),请查询
volumeAvailableCapacityForImportantUsageKey
。构建查询
使用此示例作为构建您自己的查询的指南:
volumeAvailableCapacityForOpportunisticUsageKey
let fileURL = URL(fileURLWithPath: NSHomeDirectory() as String)
do {
let values = try fileURL.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey])
if let capacity = values.volumeAvailableCapacityForImportantUsage {
print("Available capacity for important usage: \(capacity)")
} else {
print("Capacity is unavailable")
}
} catch {
print("Error retrieving capacity: \(error.localizedDescription)")
}
的可选绑定也适用于此。
我建议函数返回一个可选的if let
,以便它可以返回
Int64
表示失败:
nil
Swift 2.1更新:
func deviceRemainingFreeSpaceInBytes() -> Int64? {
let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
if let systemAttributes = NSFileManager.defaultManager().attributesOfFileSystemForPath(documentDirectoryPath.last as String, error: nil) {
if let freeSize = systemAttributes[NSFileSystemFreeSize] as? NSNumber {
return freeSize.longLongValue
}
}
// something failed
return nil
}
Swift 3.0更新:
func deviceRemainingFreeSpaceInBytes() -> Int64? {
let documentDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).last!
guard
let systemAttributes = try? NSFileManager.defaultManager().attributesOfFileSystemForPath(documentDirectory),
let freeSize = systemAttributes[NSFileSystemFreeSize] as? NSNumber
else {
// something failed
return nil
}
return freeSize.longLongValue
}
用法:
func deviceRemainingFreeSpaceInBytes() -> Int64? {
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last!
guard
let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: documentDirectory),
let freeSize = systemAttributes[.systemFreeSize] as? NSNumber
else {
// something failed
return nil
}
return freeSize.int64Value
}
答案 1 :(得分:23)
嗯,按照上面的代码:
let usedSpace = totalDiskSpaceInBytes - freeDiskSpaceInBytes
您可能会发现 usedSpace 与iPhone设置页面的值不相等。这是因为在iOS11中,Apple为“重要”资源引入了总可用容量(以字节为单位)。
“重要”资源的总可用容量(以字节为单位),包括 预期通过清除非必要和缓存来清除空间 资源。 “重要”意味着用户或应用程序 显然希望出现在本地系统上,但最终还是存在 更换。这将包括用户明确指定的项目 通过UI请求,以及应用程序所需的资源 为了提供功能。
示例:用户的视频 已明确要求观看,但尚未观看或观看 用户请求下载的音频文件。
这个值 不应该用于确定是否有空间 不可替代的资源。在不可替代的资源的情况下,永远 无论可用容量如何,都尝试保存资源 尽可能优雅地处理失败。
为了获得与我们在iPhone设置页面中看到的完全相同的值,我们可以通过 volumeAvailableCapacityForImportantUsage
获得可用空间if let space = try? URL(fileURLWithPath: NSHomeDirectory() as String).resourceValues(forKeys: [URLResourceKey.volumeAvailableCapacityForImportantUsageKey]).volumeAvailableCapacityForImportantUsage {
return space ?? 0
}
您可以使用以下UIDevice extension:
<强> Swift4 强>
extension UIDevice {
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
var totalDiskSpaceInGB:String {
return ByteCountFormatter.string(fromByteCount: totalDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.decimal)
}
var freeDiskSpaceInGB:String {
return ByteCountFormatter.string(fromByteCount: freeDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.decimal)
}
var usedDiskSpaceInGB:String {
return ByteCountFormatter.string(fromByteCount: usedDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.decimal)
}
var totalDiskSpaceInMB:String {
return MBFormatter(totalDiskSpaceInBytes)
}
var freeDiskSpaceInMB:String {
return MBFormatter(freeDiskSpaceInBytes)
}
var usedDiskSpaceInMB:String {
return MBFormatter(usedDiskSpaceInBytes)
}
//MARK: Get raw value
var totalDiskSpaceInBytes:Int64 {
guard let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String),
let space = (systemAttributes[FileAttributeKey.systemSize] as? NSNumber)?.int64Value else { return 0 }
return space
}
/*
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.
*/
var freeDiskSpaceInBytes:Int64 {
if #available(iOS 11.0, *) {
if let space = try? URL(fileURLWithPath: NSHomeDirectory() as String).resourceValues(forKeys: [URLResourceKey.volumeAvailableCapacityForImportantUsageKey]).volumeAvailableCapacityForImportantUsage {
return space ?? 0
} else {
return 0
}
} else {
if let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String),
let freeSpace = (systemAttributes[FileAttributeKey.systemFreeSize] as? NSNumber)?.int64Value {
return freeSpace
} else {
return 0
}
}
}
var usedDiskSpaceInBytes:Int64 {
return totalDiskSpaceInBytes - freeDiskSpaceInBytes
}
}
用法:
Logger.d("totalDiskSpaceInBytes: \(UIDevice.current.totalDiskSpaceInBytes)")
Logger.d("freeDiskSpace: \(UIDevice.current.freeDiskSpaceInBytes)")
Logger.d("usedDiskSpace: \(UIDevice.current.usedDiskSpaceInBytes)")
答案 2 :(得分:19)
我已经编写了一个类来使用Swift获取/使用内存。 演示于:https://github.com/thanhcuong1990/swift-disk-status
升级以支持Swift 3。
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.binary)
}
}
class var freeDiskSpace:String {
get {
return ByteCountFormatter.string(fromByteCount: freeDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.binary)
}
}
class var usedDiskSpace:String {
get {
return ByteCountFormatter.string(fromByteCount: usedDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.binary)
}
}
//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
}
}
}
演示:
答案 3 :(得分:2)
这类似于Martin对 Swift 3.1 的回答,但转换为UIDevice
的扩展名,以便更轻松地访问它。
extension UIDevice {
var systemSize: Int64? {
guard let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String),
let totalSize = (systemAttributes[.systemSize] as? NSNumber)?.int64Value else {
return nil
}
return totalSize
}
var systemFreeSize: Int64? {
guard let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String),
let freeSize = (systemAttributes[.systemFreeSize] as? NSNumber)?.int64Value else {
return nil
}
return freeSize
}
}
获得可用空间:
UIDevice.current.systemFreeSize
获得总空间:
UIDevice.current.systemSize