2017-09-24 30 views
2

我正尝试使用Swift使用系统配置框架来读取macOS上的网络接口配置。我得到的CFPropertyList其实是CFDictionary。每个字典条目都包含一个CFArray。使用CFShow我能够验证我是否获得了预期的数据。我实际上无法做的是访问字典值。使用CFGetTypeId我没有收到与CFArrayGetTypeID()返回的值相同的值。从CFPropertyList中读取值

这里是我试图让从属性列表中的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 

回答

0

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)的指针值。 你想要的是指针:

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() 
+0

我的理解是,CFDictionary'和'的NSDictionary之间'桥接'是“免费”。桥接到Swift字典会带来一些开销,在某些情况下这可能是个问题。很可能不在所描述的用例中,因为只有一个视图元素和更新非常少见。 – Bokeh

+0

@Bokeh:看起来CFPropertyList已经支持下标,所以'guard let plist = SCDynamicStoreCopyValue(dynamicStore,key)else {return}'没有这种情况。 - 我怀疑Swift桥接的成本是否相关。比较Apple Swift博客中的https://developer.apple.com/swift/blog/?id=37中的示例。 –

+0

其实我想了解如何用Core Foundation集合和原始指针来完成所有的麻烦。我遇到过他们好几次,每次遇到问题。在其他情况下,由于性能原因,可能需要直接使用CF接口。 – Bokeh