是Swift数组排序方法线程安全的本地副本的数组?



我的应用程序在下面第2行排序时发生崩溃。当NSNotification进入触发排序并刷新显示时,这发生在ViewController的方法中。

我的理解是,第1行上的devices数组是从字典创建的新数组,因此访问元素是非常安全的,即使字典可能会改变。

即使serialID(aString?)在排序过程中发生了变化,我也不认为这会导致崩溃。

我错过了什么?什么可能会导致排序中的错误访问?

1: var devices = deviceDict.valuesArray() // See Edit below
2: let sortedDevices = devices.sorted(by: { ($0.serialID ?? "") < ($1.serialID ?? "") })

我不知道如何复制这个。我只在下面这样的崩溃报告中看到过。

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000f9a70f7c0
VM Region Info: 0xf9a70f7c0 is not in any region.  Bytes after previous region: 55741315009  Bytes before following region: 630130752
REGION TYPE                 START - END      [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL
MALLOC_NANO              280000000-2a0000000 [512.0M] rw-/rwx SM=COW  
--->  GAP OF 0xd20000000 BYTES
commpage (reserved)      fc0000000-1000000000 [  1.0G] ---/--- SM=NUL  ...(unallocated)
Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [4510]
Triggered by Thread:  9
...

Thread 9 Crashed:
0   libobjc.A.dylib                 0x00000001a6ae2dd0 objc_release + 16 (objc-runtime-new.h:1589)
1   OAPP                            0x00000001044c35e4 specialized UnsafeMutableBufferPointer._stableSortImpl(by:) + 364 (DeviceViewController.swift:0)
2   OAPP                            0x00000001044c17f0 specialized MutableCollection<>.sort(by:) + 100
2   OAPP                            0x00000001044baf60 DeviceViewController.generateDeviceList() + 1644
4   OAPP                            0x00000001044b67ac DeviceViewController.refreshDeviceTableView() + 524 (DeviceViewController.swift:655)
5   OAPP                            0x00000001044bb7a4 DeviceViewController.handleSyncStateNotification(_:) + 680 (DeviceViewController.swift:821)
...

编辑:deviceDict实际上是在一个自定义类,包装一个字典一些线程安全的访问器,我把字典值转换为该类中的数组。(在我最初的帖子中,我试图简化代码,以免分散对数组操作的崩溃问题,但我似乎通过简化来混淆事情。对不起!)

public class SafeDict<Element> {
fileprivate let queue = DispatchQueue(label: "com.myapp.SafeDict", attributes: .concurrent)
fileprivate var dictionary:[String:Element] = [:] 
...
public func valuesSafe() -> Dictionary<String, Element>.Values {
var returnValues: Dictionary<String, Element>.Values = Dictionary<String, Element>().values
queue.sync {
returnValues = self.dictionary.values
}
return returnValues
}

public func valuesArray() -> [Element] {
var values: [Element] = []
queue.sync {
for element in self.valuesSafe() {
values.append(element)
}
}
return values
}
}

devices不是Array。这是Dictionary.Values。这是一个延迟视图到deviceDict

但这些都不重要。在多个线程中对deviceDict的可变访问总是未定义的行为。如果serialID可能在没有某种同步的情况下改变另一个线程,那么您已经犯了一个错误。deviceDict.values()行无效;与以后的访问无关。

但是当然sorted行更有可能出现未定义行为,因为它要遍历很多值,如果deviceDict必须调整大小或以其他方式修改其备份存储,这些值可能会移动到内存中。

最新更新