/// 线程安全数组
class ThreadSafeArray {
private let queue = DispatchQueue(label: "XNTech.ThreadSafeArray", attributes: .concurrent)
var array = [Element]()
public convenience init(_ array: [Element]) {
self.init()
self.array = array
}
}
// MARK: - Properties
extension ThreadSafeArray {
/// 原始数据
var rawData: [Element] {
self.queue.sync { return self.array }
}
/// The first element of the collection.
///
/// If the collection is empty, the value of this property is `nil`.
///
/// let numbers = [10, 20, 30, 40, 50]
/// if let firstNumber = numbers.first {
/// print(firstNumber)
/// }
/// // Prints "10"
var first: Element? {
self.queue.sync { return self.array.first }
}
/// The last element of the collection.
///
/// If the collection is empty, the value of this property is `nil`.
///
/// let numbers = [10, 20, 30, 40, 50]
/// if let lastNumber = numbers.last {
/// print(lastNumber)
/// }
/// // Prints "50"
///
/// - Complexity: O(1)
var last: Element? {
self.queue.sync { return self.array.last }
}
/// The number of elements in the array.
var count: Int {
self.queue.sync { return self.array.count }
}
/// A Boolean value indicating whether the collection is empty.
///
/// When you need to check whether your collection is empty, use the
/// `isEmpty` property instead of checking that the `count` property is
/// equal to zero. For collections that don't conform to
/// `RandomAccessCollection`, accessing the `count` property iterates
/// through the elements of the collection.
///
/// let horseName = "Silver"
/// if horseName.isEmpty {
/// print("My horse has no name.")
/// } else {
/// print("Hi ho, \(horseName)!")
/// }
/// // Prints "Hi ho, Silver!")
///
/// - Complexity: O(1)
var isEmpty: Bool {
self.queue.sync { return self.array.isEmpty }
}
/// The position of the first element in a nonempty array.
///
/// For an instance of `Array`, `startIndex` is always zero. If the array
/// is empty, `startIndex` is equal to `endIndex`.
var startIndex: Int {
self.queue.sync { return self.array.startIndex }
}
/// The array's "past the end" position---that is, the position one greater
/// than the last valid subscript argument.
///
/// When you need a range that includes the last element of an array, use the
/// half-open range operator (`..<`) with `endIndex`. The `..<` operator
/// creates a range that doesn't include the upper bound, so it's always
/// safe to use with `endIndex`. For example:
///
/// let numbers = [10, 20, 30, 40, 50]
/// if let i = numbers.firstIndex(of: 30) {
/// print(numbers[i ..< numbers.endIndex])
/// }
/// // Prints "[30, 40, 50]"
///
/// If the array is empty, `endIndex` is equal to `startIndex`.
var endIndex: Int {
self.queue.sync { return self.array.endIndex }
}
/// A textual representation of the array and its elements.
var description: String {
self.queue.sync { return self.array.description }
}
}
// MARK: - Immutable
extension ThreadSafeArray {
/// Returns the first element of the sequence that satisfies the given predicate.
///
/// - Parameter predicate: A closure that takes an element of the sequence as its argument and returns a Boolean value indicating whether the element is a match.
/// - Returns: The first element of the sequence that satisfies predicate, or nil if there is no element that satisfies predicate.
func first(where predicate: (Element) -> Bool) -> Element? {
self.queue.sync { return self.array.first(where: predicate) }
}
/// Returns the last element of the sequence that satisfies the given predicate.
///
/// - Parameter predicate: A closure that takes an element of the sequence as its argument and returns a Boolean value indicating whether the element is a match.
/// - Returns: The last element of the sequence that satisfies predicate, or nil if there is no element that satisfies predicate.
func last(where predicate: (Element) -> Bool) -> Element? {
self.queue.sync { return self.array.last(where: predicate) }
}
/// Returns an array containing, in order, the elements of the sequence that satisfy the given predicate.
///
/// - Parameter isIncluded: A closure that takes an element of the sequence as its argument and returns a Boolean value indicating whether the element should be included in the returned array.
/// - Returns: An array of the elements that includeElement allowed.
func filter(_ isIncluded: @escaping (Element) -> Bool) -> ThreadSafeArray {
self.queue.sync { return ThreadSafeArray(self.array.filter(isIncluded)) }
}
/// Returns the first index in which an element of the collection satisfies the given predicate.
///
/// - Parameter predicate: A closure that takes an element as its argument and returns a Boolean value that indicates whether the passed element represents a match.
/// - Returns: The index of the first element for which predicate returns true. If no elements in the collection satisfy the given predicate, returns nil.
func index(where predicate: (Element) -> Bool) -> Int? {
self.queue.sync { return self.array.index(where: predicate) }
}
/// Returns the elements of the collection, sorted using the given predicate as the comparison between elements.
///
/// - Parameter areInIncreasingOrder: A predicate that returns true if its first argument should be ordered before its second argument; otherwise, false.
/// - Returns: A sorted array of the collection’s elements.
func sorted(by areInIncreasingOrder: (Element, Element) -> Bool) -> ThreadSafeArray {
self.queue.sync { return ThreadSafeArray(self.array.sorted(by: areInIncreasingOrder)) }
}
/// Returns an array containing the results of mapping the given closure over the sequence’s elements.
///
/// - Parameter transform: A closure that accepts an element of this sequence as its argument and returns an optional value.
/// - Returns: An array of the non-nil results of calling transform with each element of the sequence.
func map(_ transform: @escaping (Element) -> ElementOfResult) -> [ElementOfResult] {
self.queue.sync { return self.array.map(transform) }
}
/// Returns an array containing the non-nil results of calling the given transformation with each element of this sequence.
///
/// - Parameter transform: A closure that accepts an element of this sequence as its argument and returns an optional value.
/// - Returns: An array of the non-nil results of calling transform with each element of the sequence.
func compactMap(_ transform: (Element) -> ElementOfResult?) -> [ElementOfResult] {
self.queue.sync { return self.array.compactMap(transform) }
}
/// Returns the result of combining the elements of the sequence using the given closure.
///
/// - Parameters:
/// - initialResult: The value to use as the initial accumulating value. initialResult is passed to nextPartialResult the first time the closure is executed.
/// - nextPartialResult: A closure that combines an accumulating value and an element of the sequence into a new accumulating value, to be used in the next call of the nextPartialResult closure or returned to the caller.
/// - Returns: The final accumulated value. If the sequence has no elements, the result is initialResult.
func reduce(_ initialResult: ElementOfResult, _ nextPartialResult: @escaping (ElementOfResult, Element) -> ElementOfResult) -> ElementOfResult {
var result: ElementOfResult?
self.queue.sync { result = self.array.reduce(initialResult, nextPartialResult) }
return result ?? initialResult
}
/// Returns the result of combining the elements of the sequence using the given closure.
///
/// - Parameters:
/// - initialResult: The value to use as the initial accumulating value.
/// - updateAccumulatingResult: A closure that updates the accumulating value with an element of the sequence.
/// - Returns: The final accumulated value. If the sequence has no elements, the result is initialResult.
func reduce(into initialResult: ElementOfResult, _ updateAccumulatingResult: @escaping (inout ElementOfResult, Element) -> ()) -> ElementOfResult {
var result: ElementOfResult?
self.queue.sync { result = self.array.reduce(into: initialResult, updateAccumulatingResult) }
return result ?? initialResult
}
/// Calls the given closure on each element in the sequence in the same order as a for-in loop.
///
/// - Parameter body: A closure that takes an element of the sequence as a parameter.
func forEach(_ body: (Element) -> Void) {
self.queue.sync { self.array.forEach(body) }
}
/// Returns a Boolean value indicating whether the sequence contains an element that satisfies the given predicate.
///
/// - Parameter predicate: A closure that takes an element of the sequence as its argument and returns a Boolean value that indicates whether the passed element represents a match.
/// - Returns: true if the sequence contains an element that satisfies predicate; otherwise, false.
func contains(where predicate: (Element) -> Bool) -> Bool {
self.queue.sync { return self.array.contains(where: predicate) }
}
/// Returns a Boolean value indicating whether every element of a sequence satisfies a given predicate.
///
/// - Parameter predicate: A closure that takes an element of the sequence as its argument and returns a Boolean value that indicates whether the passed element satisfies a condition.
/// - Returns: true if the sequence contains only elements that satisfy predicate; otherwise, false.
func allSatisfy(_ predicate: (Element) -> Bool) -> Bool {
self.queue.sync { return self.array.allSatisfy(predicate) }
}
}
// MARK: - Mutable
extension ThreadSafeArray {
/// Adds a new element at the end of the array.
///
/// - Parameter element: The element to append to the array.
func append(_ element: Element) {
self.queue.async(flags: .barrier) { [weak self] in
guard let self = self else { return }
self.array.append(element)
}
}
/// Adds new elements at the end of the array.
///
/// - Parameter element: The elements to append to the array.
func append(_ elements: [Element]) {
self.queue.async(flags: .barrier) { [weak self] in
guard let self = self else { return }
self.array += elements
}
}
/// Inserts a new element at the specified position.
///
/// - Parameters:
/// - element: The new element to insert into the array.
/// - index: The position at which to insert the new element.
func insert(_ element: Element, at index: Int) {
self.queue.async(flags: .barrier) { [weak self] in
guard let self = self else { return }
self.array.insert(element, at: index)
}
}
/// Removes and returns the element at the specified position.
///
/// - Parameters:
/// - index: The position of the element to remove.
/// - completion: The handler with the removed element.
func remove(at index: Int, completion: ((Element) -> Void)? = nil) {
self.queue.async(flags: .barrier) { [weak self] in
guard let self = self else { return }
let element = self.array.remove(at: index)
DispatchQueue.main.async { completion?(element) }
}
}
/// Removes and returns the elements that meet the criteria.
///
/// - Parameters:
/// - predicate: A closure that takes an element of the sequence as its argument and returns a Boolean value indicating whether the element is a match.
/// - completion: The handler with the removed elements.
func remove(where predicate: @escaping (Element) -> Bool, completion: (([Element]) -> Void)? = nil) {
self.queue.async(flags: .barrier) { [weak self] in
guard let self = self else { return }
var elements = [Element]()
while let index = self.array.index(where: predicate) {
elements.append(self.array.remove(at: index))
}
DispatchQueue.main.async { completion?(elements) }
}
}
/// Removes all elements from the array.
///
/// - Parameter completion: The handler with the removed elements.
func removeAll(completion: (([Element]) -> Void)? = nil) {
self.queue.async(flags: .barrier) { [weak self] in
guard let self = self else { return }
let elements = self.array
self.array.removeAll()
DispatchQueue.main.async { completion?(elements) }
}
}
}
extension ThreadSafeArray {
/// Accesses the element at the specified position if it exists.
///
/// - Parameter index: The position of the element to access.
/// - Returns: optional element if it exists.
subscript(index: Int) -> Element? {
get {
var result: Element?
self.queue.sync {
guard self.array.startIndex.. Bool {
var result = false
self.queue.sync { result = self.array.contains(element) }
return result
}
}
// MARK: - Infix operators
extension ThreadSafeArray {
/// Adds a new element at the end of the array.
///
/// - Parameters:
/// - left: The collection to append to.
/// - right: The element to append to the array.
static func +=(left: inout ThreadSafeArray, right: Element) {
left.append(right)
}
/// Adds new elements at the end of the array.
///
/// - Parameters:
/// - left: The collection to append to.
/// - right: The elements to append to the array.
static func +=(left: inout ThreadSafeArray, right: [Element]) {
left.append(right)
}
}