macOS(Swift)下无需diskutil CLI包装器获取BSD驱动器名称
在macOS Swift开发中获取所有USB/内置磁盘BSD名称的最优方案(不含CLI包装器)
问题描述
需要在macOS平台用Swift开发,完全不依赖diskutil CLI包装器获取所有USB设备(包含Mac内置磁盘)的BSD名称列表,要求同时覆盖已挂载和已弹出的设备。当前实现的代码仅能返回已挂载设备,希望找到更高效可靠的方案(比如基于IOReg或Disk Arbitration),因为CLI交互方式速度慢且稳定性差。
期望返回结果示例:
["disk0", "disk0s1", "disk0s2", "disk0s3", "disk1", "disk1s1", "disk1s2", "disk1s3", "disk1s4", "disk2", "disk2s1", "disk2s2", "disk3", "disk3s1", "disk3s1s1", "disk3s2", "disk3s3", "disk3s4", "disk3s5", "disk3s6", "disk4", "disk4s1", "disk4s2", "disk5", "disk5s1", "disk5s2", "disk6", "disk6s1", "disk6s2", "disk10", "disk10s1", "disk10s2", "disk11", "disk11s1"]
当前仅能获取已挂载设备的代码:
static func getMountedBSDNames() -> [String] { guard let session = DASessionCreate(nil) else { return [] } guard let mountedVolumeURLs = FileManager.default.mountedVolumeURLs(includingResourceValuesForKeys: nil) else { return [] } var BSDNames: [String] = [] for volumeURL in mountedVolumeURLs { if let disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, volumeURL as CFURL), let BSDName = DADiskGetBSDName(disk) { BSDNames.append( String(cString: BSDName) ) } } return BSDNames }
最优实现方案
方案1:基于Disk Arbitration枚举所有磁盘(推荐)
Disk Arbitration框架不仅能处理已挂载卷,还能枚举系统中所有已连接的磁盘设备(包括弹出但未物理移除的),无需依赖CLI,性能稳定:
import DiskArbitration import Foundation static func getAllBSDNames() -> [String] { guard let session = DASessionCreate(nil) else { return [] } var bsdnNames: [String] = [] // 设置回调捕获所有磁盘设备 DASessionSetDispatchQueue(session, DispatchQueue.global()) DARegisterDiskAppearedCallback(session, nil) { _, diskRef in guard let bsdName = DADiskGetBSDName(diskRef) else { return } let name = String(cString: bsdName) if !bsdnNames.contains(name) { bsdnNames.append(name) } } // 触发初始设备枚举 DASessionRun(session, 0) DASessionStop(session) return bsdnNames }
方案2:基于IORegistry底层查询
如果需要更深度的设备控制,可以直接查询IORegistry,获取系统中所有磁盘设备的BSD名称,包括部分Disk Arbitration未主动枚举的设备:
import IOKit import Foundation static func getAllBSDNamesFromIOReg() -> [String] { var bsdnNames: [String] = [] // 获取磁盘设备的IOReg迭代器 guard let iterator = IOServiceGetMatchingIterator(kIOMainPortDefault, IOServiceMatching(kIOMediaClass)) else { return [] } defer { IOObjectRelease(iterator) } var ioObject: io_object_t = 0 while IOIteratorNext(iterator, &ioObject) == KERN_SUCCESS { defer { IOObjectRelease(ioObject) } // 提取BSD名称属性 if let bsdName = IORegistryEntryCreateCFProperty(ioObject, kIOBSDNameKey as CFString, kCFAllocatorDefault, 0)?.takeRetainedValue() as? String { bsdnNames.append(bsdName) } } return bsdnNames }
方案对比
- Disk Arbitration方案:符合系统框架设计,自动处理设备热插拔事件,代码简洁易维护,适合绝大多数场景。
- IORegistry方案:更底层,能覆盖更多边缘设备,但需要手动管理内存和迭代逻辑,适合需要深度定制的场景。
额外扩展:筛选USB设备
如果需要仅保留USB设备,可以在枚举时添加属性判断:
- Disk Arbitration:通过
DADiskGetDescription(diskRef)获取设备属性,检查是否属于USB总线; - IORegistry:通过
IORegistryEntrySearchCFProperty查找设备的USB父路径,判断设备类型。
内容的提问来源于stack exchange,提问作者TechUnRestricted
相关产品推荐
相关产品推荐

