Как построить UnsafeMutablePointerнабрать в Swift? - PullRequest
1 голос
/ 27 апреля 2019

Я взаимодействую с Core MIDI API с помощью Swift и у меня возникли некоторые проблемы с функцией MIDIThruConnectionFind.

В документации указано следующее

func MIDIThruConnectionFind(_ inPersistentOwnerID: CFString, 
                      _ outConnectionList: UnsafeMutablePointer<Unmanaged<CFData>>) -> OSStatus

Это моя функция, и что бы я ни пытался, я получаю ошибки сборки.Например, переменная используется, но не инициализируется, имеет неправильный тип и т. Д.

@IBAction func listConnections(_ sender: Any) {
    var connectionRef: Unmanaged<CFData>

    MIDIThruConnectionFind("" as CFString, &connectionRef)        
}

Я ожидаю, что мне нужно указать адрес указателя для outConnectionList и что функция выделяет памятьдля данных.Но как мне сделать это в Swift?

Обновление

По крайней мере, это компилируется, но как отменить ссылку и получить доступ к данным?

@IBAction func listConnections(_ sender: Any) {
    let connectionRefs = UnsafeMutablePointer<Unmanaged<CFData>>.allocate(capacity: 1)

    MIDIThruConnectionFind("" as CFString, connectionRefs)
}

1 Ответ

1 голос
/ 27 апреля 2019

Я немного догадываюсь и не могу на самом деле проверить код в данный момент, но вот мои мысли:

Функция MIDIThruConnectionFind() объявлена ​​в MIDIThruConnection.h как

extern OSStatus
MIDIThruConnectionFind(     CFStringRef                     inPersistentOwnerID,
                            CFDataRef __nonnull * __nonnull outConnectionList )

и поэтому импортируется в Swift как

public func MIDIThruConnectionFind(_ inPersistentOwnerID: CFString,
            _ outConnectionList: UnsafeMutablePointer<Unmanaged<CFData>>) -> OSStatus

, что означает, что последний параметр должен быть адресом (инициализированного и) необязательного Unmanaged<CFData> значения.

Но это не имеет смысла: данные распределяются функцией, и мы не хотим передавать какие-либо данные. Я настоятельно предполагаю, что это ошибка в аннотации обнуляемости этой функции в заголовке C. Другие основные MIDI-функции с параметром out правильно аннотированы, например,

extern OSStatus
MIDIObjectGetStringProperty(    MIDIObjectRef           obj,
                                CFStringRef             propertyID,
                                CFStringRef __nullable * __nonnull str )        

Может работать следующий обходной путь: Объявите connectionRef как необязательный указатель (так, чтобы он был инициализирован как nil), и «приведите» его к необязательному указателю при вызове функции :

var connectionRef: Unmanaged<CFData>?

let status = withUnsafeMutablePointer(to: &connectionRef) {
    $0.withMemoryRebound(to: Unmanaged<CFData>.self, capacity: 1) {
        MIDIThruConnectionFind("" as CFString, $0)
    }
}

Если это удастся, необязательный указатель может быть развернут, и ссылка CFData получена с помощью takeRetainedValue(). CFData предоставляется по бесплатному мосту NSData, и его можно привести к типу оверлея Swift Data:

if status == noErr, let connectionRef = connectionRef {
    let data = connectionRef.takeRetainedValue() as Data

}

Другим обходным решением является определение функции-оболочки с правильными аннотациями обнуляемости в файле заголовка моста:

#include <CoreMIDI/CoreMIDI.h>

static OSStatus myMIDIThruConnectionFind(CFStringRef inPersistentOwnerID,
                                          CFDataRef __nullable * __nonnull outConnectionList) {
    return MIDIThruConnectionFind(inPersistentOwnerID, outConnectionList);
}

, который затем можно назвать

var connectionRef: Unmanaged<CFData>?
let status = myMIDIThruConnectionFind("" as CFString, &connectionRef)
...