Как вы проверяете, существует ли ключ неверен. Вы каждый раз создаете новую очередь, а это означает, что операции не происходят синхронно.
Как я хотел бы сделать это, как так:
class Some {}
class Test {
var dict = [String: Some]()
private let queue: dispatch_queue_t = dispatch_queue_create("has", DISPATCH_QUEUE_CONCURRENT)
func has(key: String) -> Bool {
var has = false
dispatch_sync(queue) { [weak self] in
guard let strongSelf = self else { return }
has = strongSelf.dict[key] != nil
}
return has
}
func remove(key: String) {
dispatch_barrier_async(queue) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.dict[key] = nil
}
}
func add(key: String, ob: Some) {
dispatch_barrier_async(queue) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.dict[key] = ob
}
}
}
Во-первых, я создаю последовательный очереди, которая будет использоваться для доступа к словарю как свойство объекта, а не создавая новый один каждый раз. Очередь является частной, поскольку она используется только внутри страны.
Когда я хочу получить значение из класса, я просто отправляю блок синхронно в очередь и жду, пока блок закончит работу, прежде чем возвращать, существует ли очередь. Поскольку это не мутирует словарь, безопасно, чтобы несколько блоков этого типа выполнялись в параллельной очереди.
Когда я хочу добавить или удалить значения из словаря, я добавляю блок в очередь, но с барьером. Это означает, что он останавливает все остальные блоки в очереди во время работы. Когда все будет готово, все остальные блоки могут работать одновременно. Я использую асинхронную отправку, потому что мне не нужно ждать возвращаемого значения.
Представьте, что у вас есть несколько потоков, пытаясь определить, существуют ли ключевые значения или добавляются или удаляются значения. Если у вас много чтений, они происходят одновременно, но когда выполняется один из блоков, который изменит словарь, все остальные блоки ожидают, пока это изменение не будет завершено, а затем снова запустится.
Таким образом, вы получаете скорость и удобство одновременного запуска при получении значений и безопасность потока при блокировке во время мутации словаря.
Edited добавить
self
отмечен как weak
в блоке, так что он не создает опорный цикл. Как упоминал @MartinR в комментариях; возможно, что объект освобождается, пока блоки все еще находятся в очереди. Если это произойдет, то self не определено, и вы, вероятно, получите ошибку времени выполнения, пытающуюся получить доступ к словарю, так как она также может быть освобождена.
Установив объявление self
внутри блока, будет слабым, если объект существует, то self
не будет нулевым, и может быть условно развернул в strongSelf
, что указует на self
, а также создает сильную ссылку, так что self
не будет освобождаются, пока выполняются инструкции в блоке. Когда эти инструкции будут завершены, strongSelf
выйдет за рамки и высвободит сильную ссылку на себя.
Это иногда называют «сильным« я », слабым таинством».
Edited Again: Swift 3 версия
class Some {}
class Test {
var dict = [String: Some]()
private let queue = DispatchQueue(label: "has", qos: .default, attributes: .concurrent)
func has(key: String) -> Bool {
var has = false
queue.sync { [weak self] in
guard let strongSelf = self else { return }
has = strongSelf.dict[key] != nil
}
return has
}
func remove(key: String) {
queue.async(flags: .barrier) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.dict[key] = nil
}
}
func add(key: String, ob: Some) {
queue.async(flags: .barrier) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.dict[key] = ob
}
}
}
Отличный ответ! Один вопрос: не было бы «[слабым»] безопаснее? Объект 'Test' может быть освобожден при выполнении барьерного блока. –
@MartinR В этом случае не будет ли освобождена очередь, в которой работает блок? – Abizern
Из [dispatch_barrier_async()] (https://developer.apple.com/library/mac/documentation/Performance/Reference/GCD_libdispatch_Ref/index.html#//apple_ref/c/func/dispatch_barrier_async): * «Очередь сохраняемый системой до тех пор, пока блок не завершится ». * - Итак, я думаю, что даже если объект будет освобожден и высвободит свою ссылку на очередь, очередь будет продолжаться до тех пор, пока не будет завершен весь незавершенный блок. –