小工具-线程安全数组

/// 线程安全数组 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) } }