2017-09-24 2 views
2

J'essaie de lire la configuration de l'interface réseau sur macOS en utilisant le cadre de configuration du système avec Swift. Je reçois un CFPropertyList qui est en fait un CFDictionary. Chaque entrée de dictionnaire contient un CFArray. Avec CFShow, j'ai pu vérifier que j'avais les données attendues. Ce que je ne peux pas faire, c'est accéder aux valeurs du dictionnaire. Avec CFGetTypeId je ne reçois pas la même valeur que renvoyée par CFArrayGetTypeID().Lecture des valeurs de CFPropertyList

Voici ce que j'ai essayé d'obtenir l'adresse IP de la liste des biens:

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!)") 
} 

La sortie est la suivante:

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 

Répondre

0

CFPropertyList soutient déjà subscripting, donc il n'y a pas besoin de tracas avec CFDictionary et pointeurs bruts, vous pouvez simplifier le code à

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) 
} 

Mais si vous êtes vraiment curieux comment faire votre travail d'approche basée CFDictionary/CFArray : Le problème est que

let anyRef: CFTypeRef = rawPointerToValue as CFTypeRef 

enveloppements la valeur du pointeur dans une CFTypeRef (aka AnyObject). Qu'est-ce que vous voulez est de cast le pointeur:

let anyRef = unsafeBitCast(rawPointerToValue!, to: CFTypeRef.self) 
guard CFGetTypeID(anyRef) == CFArrayGetTypeID() else { return } 
let cfArray = anyRef as! CFArray 
// ... 

autre (équivalent) façon de faire la distribution de pointeur est

let anyRef = Unmanaged<CFTypeRef>.fromOpaque(rawPointerToValue!).takeUnretainedValue() 
+0

Ce que je compris que le pont entre 'CFDictionary' et' NSDictionary 'est" sans frais ". Le raccordement à un dictionnaire Swift s'accompagne de certains frais généraux, ce qui peut poser problème dans certains cas. Bien probablement pas dans le cas d'utilisation décrit, car il n'y a qu'une vue les éléments et les mises à jour sont assez rares. – Bokeh

+0

@Bokeh: Il semble que CFPropertyList supporte déjà l'indice, donc 'guard let plist = SCDynamicStoreCopyValue (dynamicStore, key) sinon {return}' sans que le cas ne fonctionne aussi bien. - Et je doute que ce coût du pont Swift soit pertinent. Comparez les exemples sur https://developer.apple.com/swift/blog/?id=37 du blog Apple Swift. –

+0

En fait, je voudrais comprendre comment faire tout le tracas avec les collections Core Foundation et les pointeurs bruts. Je suis tombé sur eux plusieurs fois et chaque fois j'ai des problèmes. Dans d'autres cas, il peut être nécessaire d'utiliser directement l'interface CF pour des raisons de performances. – Bokeh