Можем ли мы использовать API на основе C-структур непосредственно из Swift?

Я ушел с WWDC 2016 с пониманием того, что нам следует опасаться использования C-based structAPI прямо из Swift. В параллельном программировании с GCD в Swift 3, говоря о блокировках на основе C, они были очень конкретными:

... А в Swift, поскольку у вас есть весь модуль Darwin, вы фактически увидите structна основе традиционных замков C. Однако Свифт предполагает, что все, чтоstructможно перемещать, и это не работает с мьютексом или блокировкой. Поэтому мы действительно не рекомендуем вам использовать подобные блокировки от Swift....

... И если вам нужно что-то... похожее на блокировки, которые есть у вас в C, тогда вам нужно вызвать Objective-C и ввести базовый класс в Objective-C, который имеет вашу блокировку как ivar.

А потом выставишь lock а также unlock методы и tryLockесли он вам тоже нужен, вы сможете вызывать его из Swift, когда создадите подкласс этого класса....

@implementation LockableObject {
    os_unfair_lock _lock;
}

- (void)lock   { os_unfair_lock_lock(&_lock); }
- (void)unlock { os_unfair_lock_unlock(&_lock); }
@end

Однако, наблюдая за WWDC 2019 Developing a Great Profiling Experience, я заметил, что автор используетos_unfair_lock непосредственно из Swift, без этой оболочки Objective-C, эффективно:

private var workItemsLock = os_unfair_lock()

func subWorkItem(...) {
    ...
    os_unfair_lock_lock(&self.workItemsLock)
    ...
    os_unfair_lock_unlock(&self.workItemsLock)
    ...
}

Эмпирически такое прямое использование os_unfair_lockпохоже, работает, но это ничего не значит. Принимая во внимание предостережение в видео WWDC 2016 года, я воздержался от использованияos_unfair_lock прямо из Swift.

Итак, вопрос в том, являются ли они (хоть немного) небрежными в использовании этого API в этом образце 2019 года? Или видео 2016 года было неверным в своих претензиях? Или имеет обработку на основе Cstruct изменился со времен Swift 3, теперь этот шаблон становится безопасным?

1 ответ

Решение

Пример использования API private var workItemsLock = os_unfair_lock() может честно во время выполнения.

Примитивы потоковой передачи из C нуждаются в стабильной области памяти, поэтому, чтобы использовать их или другую структуру, которая имеет один из этих примитивов непосредственно в качестве члена, вы должны использовать UnsafePointer. Причина в том, чтоUnsafePointer API-интерфейсы после того, как они выделили кусок памяти, эта память стабильна и не может быть перемещена или тривиально скопирована компилятором.

Если вы измените пример таким образом, он теперь действителен

private var workItemsLock: UnsafeMutablePointer<os_unfair_lock> = {
    // Note, somewhere else this will need to be deallocated
    var pointer = UnsafeMutablePointer<os_unfair_lock>.allocate(capacity: 1)
    pointer.initialize(to: os_unfair_lock())
    return pointer
}()

func subWorkItem(...) {
    ...
    os_unfair_lock_lock(self.workItemsLock)
    ...
    os_unfair_lock_unlock(self.workItemsLock)
    ...
}
Другие вопросы по тегам