从CFPropertyList读取值

时间:2017-09-24 15:22:06

标签: swift macos cocoa core-foundation

我正在尝试使用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

1 个答案:

答案 0 :(得分: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()