我从WWDC 2016离开后,了解到我们应该警惕直接从Swift使用基于C的struct
API。在Concurrent Programming With GCD in Swift 3中,谈到基于C的锁,它们非常具体:
...在Swift中,由于您拥有整个Darwin模块,因此您实际上会看到基于
struct
的传统C锁。但是,Swift假定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,我注意到作者实际上是直接从Swift使用os_unfair_lock
,而没有这个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
似乎可行,但这并不意味着什么。尊重2016年WWDC视频中的警告,我避免直接使用Swift的os_unfair_lock
。
所以,问题是,在这个2019年的示例中,使用此API时他们是否太草率?还是2016年影片的版权声明不正确?还是自Swift 3起就改变了基于C的struct
的处理方式,现在使该模式变得安全了?
使用private var workItemsLock = os_unfair_lock()
的API示例可以在运行时公平。
来自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)
...
}