Ошибка плохого доступа при вызове закрытия Swift из C api в Какао - PullRequest
2 голосов
/ 18 марта 2019

Я пытаюсь реализовать «копию файла с прогрессом» в Swift на macOS.После долгих поисков я обнаружил орудие шороха в Objective-C .Это работает довольно хорошо.Но я бы хотел, чтобы это было "быстро".Я попробовал это с некоторым упрощенным кодом:

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    var copyfileCallback: copyfile_callback_t = {(what, stage, state, sourcePath, destPath, context) -> Int32 in
        return COPYFILE_CONTINUE
    }

    @IBOutlet weak var window: NSWindow!

    func applicationDidFinishLaunching(_ aNotification: Notification) {

        let src = NSURL(fileURLWithPath: "Source_File_Path").fileSystemRepresentation
        let dst = NSURL(fileURLWithPath: "Destination_File_Path").fileSystemRepresentation
        let flag: copyfile_flags_t = UInt32(COPYFILE_ALL)

        let state = copyfile_state_alloc()

        // If I implement this, the copyfile() method will complain "EXC_BAD_ACCESS(code=2..." error
        copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CB), &copyfileCallback)

        copyfile(src, dst, state, flag)
    }
}

Базовая функция copyfile() работает отлично.Но если я реализую функцию обратного вызова, предоставив указатель закрытия copyfileCallback на copyfile_state_set(), то copyfile() просто жалуется на «Bad_Access ...».Я думаю, может быть закрытие было выпущено до того, как C api пытается получить к нему доступ.Но я понятия не имею, как решить эту проблему ... Любая подсказка будет так ценится.

1 Ответ

1 голос
/ 18 марта 2019

Ошибка здесь:

copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CB), &copyfileCallback)

потому что передает адрес переменной copyfileCallback функции, а не самому указателю функции. В C вы можете передать произвольную функцию в качестве аргумента void *. В Swift вы должны явно привести функцию к указателю:

let state = copyfile_state_alloc()
copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CB),
                   unsafeBitCast(copyfileCallback, to: UnsafeRawPointer.self))

И не забудьте в конце концов освободить память после операции копирования:

copyfile_state_free(state)

Примечание: В Swift рекомендуется использовать (тип наложения значения) URL вместо NSURL:

let srcURL = URL(fileURLWithPath: "Source_File_Path")
let destURL = URL(fileURLWithPath: "Destination_File_Path")

let result = srcURL.withUnsafeFileSystemRepresentation { srcFile in
    destURL.withUnsafeFileSystemRepresentation { destFile in
        copyfile(srcFile, destFile, state, flag)
    }
}
...