从 CFPropertyList 读取值
Reading values from CFPropertyList
我正在尝试使用带 Swift 的系统配置框架来读取 macOS 上的网络接口配置。我得到一个 CFPropertyList
,它实际上是一个 CFDictionary
。每个字典条目都包含一个 CFArray
。使用 CFShow
我能够验证我是否获得了预期的数据。我实际上无法做的是访问字典值。使用 CFGetTypeId
我得到的值与 CFArrayGetTypeID()
返回的值不同。
这是我试图从 属性 列表中获取 IP 地址的方法:
import SystemConfiguration
func readIP() {
let cfName = "Demo" as CFString
let dynamicStore = SCDynamicStoreCreate(nil, cfName, nil, nil)
let key = "State:/Network/Interface/en0/IPv4" as CFString
guard let plist: CFPropertyList = SCDynamicStoreCopyValue(dynamicStore, key) else {return}
print("CFShow(plist):")
CFShow(plist)
print("Key: \(key):")
print("Value: \(plist)")
let typeIdPList = CFGetTypeID(plist)
let typeIdDictionary = CFDictionaryGetTypeID()
print("typeIdPList: \(typeIdPList)")
print("typeIdDictionary: \(typeIdDictionary)")
guard typeIdPList == typeIdDictionary else {return}
let cfDict: CFDictionary = plist as! CFDictionary
let rawPointerToKeyAddresses = Unmanaged.passUnretained(kSCPropNetIPv4Addresses).toOpaque()
var rawPointerToValue: UnsafeRawPointer?
guard CFDictionaryGetValueIfPresent(cfDict, rawPointerToKeyAddresses, &rawPointerToValue) == true else {return}
let anyRef: CFTypeRef = rawPointerToValue as CFTypeRef
print("Value:")
CFShow(anyRef)
let typeIdValue = CFGetTypeID(anyRef)
let typeIdArray = CFArrayGetTypeID()
print("typeIdValue: \(typeIdValue)")
print("typeIdArray: \(typeIdArray)")
let cfArray: CFArray = anyRef as! CFArray
let typeId = CFGetTypeID(cfArray)
print("typeId: \(typeId)")
let desc = CFCopyDescription(anyRef)
let typeDesc = CFCopyTypeIDDescription(CFGetTypeID(anyRef))
print("CFTypeRef description: \(desc!)")
print("CFTypeRef type description: \(typeDesc!)")
}
输出结果如下:
CFShow(plist):
<CFBasicHash 0x610000069500 [0x7fffc4383da0]>{type = immutable dict, count = 3, entries =>
0 : Addresses = <CFArray 0x610000069440 [0x7fffc4383da0]>{type = immutable, count = 1, values = (
0 : <CFString 0x610000028980 [0x7fffc4383da0]>{contents = "192.168.139.24"}
)}
1 : <CFString 0x610000044c20 [0x7fffc4383da0]>{contents = "BroadcastAddresses"} = <CFArray 0x610000069480 [0x7fffc4383da0]>{type = immutable, count = 1, values = (
0 : <CFString 0x610000044c50 [0x7fffc4383da0]>{contents = "192.168.139.255"}
)}
2 : <CFString 0x7fffc429d300 [0x7fffc4383da0]>{contents = "SubnetMasks"} = <CFArray 0x6100000694c0 [0x7fffc4383da0]>{type = immutable, count = 1, values = (
0 : <CFString 0x6100000289a0 [0x7fffc4383da0]>{contents = "255.255.255.0"}
)}
}
Key: State:/Network/Interface/en0/IPv4:
Value: {
Addresses = (
"192.168.139.24"
);
BroadcastAddresses = (
"192.168.139.255"
);
SubnetMasks = (
"255.255.255.0"
);
}
typeIdPList: 18
typeIdDictionary: 18
Value:
0x0000610000069440
typeIdValue: 1
typeIdArray: 19
typeId: 1
CFTypeRef description: 0x0000610000069440
CFTypeRef type description: CFType
CFPropertyList
已经支持下标了,所以不用纠结CFDictionary
和原始指针,可以把代码简化为
let cfName = "Demo" as CFString
let dynamicStore = SCDynamicStoreCreate(nil, cfName, nil, nil)
let key = "State:/Network/Interface/en0/IPv4" as CFString
guard let plist = SCDynamicStoreCopyValue(dynamicStore, key) else {return}
print(plist)
if let addresses = plist[kSCPropNetIPv4Addresses] as? [String] {
print(addresses)
}
但是如果你真的很好奇如何制作你的 CFDictionary
/CFArray
基于方法的工作:问题是
let anyRef: CFTypeRef = rawPointerToValue as CFTypeRef
将指针值包装到CFTypeRef
(又名AnyObject
)。
你想要的是 cast 指针:
let anyRef = unsafeBitCast(rawPointerToValue!, to: CFTypeRef.self)
guard CFGetTypeID(anyRef) == CFArrayGetTypeID() else { return }
let cfArray = anyRef as! CFArray
// ...
进行指针转换的另一种(等效)方法是
let anyRef = Unmanaged<CFTypeRef>.fromOpaque(rawPointerToValue!).takeUnretainedValue()
我正在尝试使用带 Swift 的系统配置框架来读取 macOS 上的网络接口配置。我得到一个 CFPropertyList
,它实际上是一个 CFDictionary
。每个字典条目都包含一个 CFArray
。使用 CFShow
我能够验证我是否获得了预期的数据。我实际上无法做的是访问字典值。使用 CFGetTypeId
我得到的值与 CFArrayGetTypeID()
返回的值不同。
这是我试图从 属性 列表中获取 IP 地址的方法:
import SystemConfiguration
func readIP() {
let cfName = "Demo" as CFString
let dynamicStore = SCDynamicStoreCreate(nil, cfName, nil, nil)
let key = "State:/Network/Interface/en0/IPv4" as CFString
guard let plist: CFPropertyList = SCDynamicStoreCopyValue(dynamicStore, key) else {return}
print("CFShow(plist):")
CFShow(plist)
print("Key: \(key):")
print("Value: \(plist)")
let typeIdPList = CFGetTypeID(plist)
let typeIdDictionary = CFDictionaryGetTypeID()
print("typeIdPList: \(typeIdPList)")
print("typeIdDictionary: \(typeIdDictionary)")
guard typeIdPList == typeIdDictionary else {return}
let cfDict: CFDictionary = plist as! CFDictionary
let rawPointerToKeyAddresses = Unmanaged.passUnretained(kSCPropNetIPv4Addresses).toOpaque()
var rawPointerToValue: UnsafeRawPointer?
guard CFDictionaryGetValueIfPresent(cfDict, rawPointerToKeyAddresses, &rawPointerToValue) == true else {return}
let anyRef: CFTypeRef = rawPointerToValue as CFTypeRef
print("Value:")
CFShow(anyRef)
let typeIdValue = CFGetTypeID(anyRef)
let typeIdArray = CFArrayGetTypeID()
print("typeIdValue: \(typeIdValue)")
print("typeIdArray: \(typeIdArray)")
let cfArray: CFArray = anyRef as! CFArray
let typeId = CFGetTypeID(cfArray)
print("typeId: \(typeId)")
let desc = CFCopyDescription(anyRef)
let typeDesc = CFCopyTypeIDDescription(CFGetTypeID(anyRef))
print("CFTypeRef description: \(desc!)")
print("CFTypeRef type description: \(typeDesc!)")
}
输出结果如下:
CFShow(plist):
<CFBasicHash 0x610000069500 [0x7fffc4383da0]>{type = immutable dict, count = 3, entries =>
0 : Addresses = <CFArray 0x610000069440 [0x7fffc4383da0]>{type = immutable, count = 1, values = (
0 : <CFString 0x610000028980 [0x7fffc4383da0]>{contents = "192.168.139.24"}
)}
1 : <CFString 0x610000044c20 [0x7fffc4383da0]>{contents = "BroadcastAddresses"} = <CFArray 0x610000069480 [0x7fffc4383da0]>{type = immutable, count = 1, values = (
0 : <CFString 0x610000044c50 [0x7fffc4383da0]>{contents = "192.168.139.255"}
)}
2 : <CFString 0x7fffc429d300 [0x7fffc4383da0]>{contents = "SubnetMasks"} = <CFArray 0x6100000694c0 [0x7fffc4383da0]>{type = immutable, count = 1, values = (
0 : <CFString 0x6100000289a0 [0x7fffc4383da0]>{contents = "255.255.255.0"}
)}
}
Key: State:/Network/Interface/en0/IPv4:
Value: {
Addresses = (
"192.168.139.24"
);
BroadcastAddresses = (
"192.168.139.255"
);
SubnetMasks = (
"255.255.255.0"
);
}
typeIdPList: 18
typeIdDictionary: 18
Value:
0x0000610000069440
typeIdValue: 1
typeIdArray: 19
typeId: 1
CFTypeRef description: 0x0000610000069440
CFTypeRef type description: CFType
CFPropertyList
已经支持下标了,所以不用纠结CFDictionary
和原始指针,可以把代码简化为
let cfName = "Demo" as CFString
let dynamicStore = SCDynamicStoreCreate(nil, cfName, nil, nil)
let key = "State:/Network/Interface/en0/IPv4" as CFString
guard let plist = SCDynamicStoreCopyValue(dynamicStore, key) else {return}
print(plist)
if let addresses = plist[kSCPropNetIPv4Addresses] as? [String] {
print(addresses)
}
但是如果你真的很好奇如何制作你的 CFDictionary
/CFArray
基于方法的工作:问题是
let anyRef: CFTypeRef = rawPointerToValue as CFTypeRef
将指针值包装到CFTypeRef
(又名AnyObject
)。
你想要的是 cast 指针:
let anyRef = unsafeBitCast(rawPointerToValue!, to: CFTypeRef.self)
guard CFGetTypeID(anyRef) == CFArrayGetTypeID() else { return }
let cfArray = anyRef as! CFArray
// ...
进行指针转换的另一种(等效)方法是
let anyRef = Unmanaged<CFTypeRef>.fromOpaque(rawPointerToValue!).takeUnretainedValue()