diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7b5954a7d..e14a8ab8e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -68,10 +68,10 @@ jobs: swiftpm-linux: strategy: matrix: - swift: ["5.2", "5.7"] + swift: ["5.7"] name: SwiftPM Linux - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Setup Swift version uses: swift-actions/setup-swift@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index e2952609f..ed97c225c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ 1. Fix minimum deployment target of iOS 11 in CocoaPods 1. Fix CI release git tag push trigger (#869, kudos to @p4checo) +1. Find and remove items from Bag using a binary search to improve performance when the collection gets large. +2. Add extension to `ScopedDisposable` for inner `SerialDisposable` (#873, kudos to @sirnacnud) # 7.1.1 1. Bumped deployment target to iOS 11, tvOS 11, watchOS 4, macOS 10.13, per Xcode 14 warnings (#865, kudos to @lickel) diff --git a/ReactiveSwift-UIExamples.playground/Pages/ValidatingProperty.xcplaygroundpage/Contents.swift b/ReactiveSwift-UIExamples.playground/Pages/ValidatingProperty.xcplaygroundpage/Contents.swift index aaa837d91..0bca2c2e2 100644 --- a/ReactiveSwift-UIExamples.playground/Pages/ValidatingProperty.xcplaygroundpage/Contents.swift +++ b/ReactiveSwift-UIExamples.playground/Pages/ValidatingProperty.xcplaygroundpage/Contents.swift @@ -6,7 +6,6 @@ **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - `carthage checkout` 1. Open `ReactiveSwift.xcworkspace` - 1. Build `Result-iOS` scheme 1. Build `ReactiveSwift-iOS` scheme 1. Finally open the `ReactiveSwift-UIExamples.playground` through the workspace. 1. Choose `View > Assistant Editor > Show Assistant Editor` diff --git a/Sources/Bag.swift b/Sources/Bag.swift index e38b92b04..163b6d7d2 100644 --- a/Sources/Bag.swift +++ b/Sources/Bag.swift @@ -58,13 +58,45 @@ public struct Bag { /// - token: A token returned from a call to `insert()`. @discardableResult public mutating func remove(using token: Token) -> Element? { - guard let index = indices.first(where: { tokens[$0] == token.value }) else { + // Given that tokens are always added to the end of the array and have a monotonically + // increasing value, this list is always sorted, so we can use a binary search to improve + // performance if this list gets large. + guard let index = binarySearch(tokens, value: token.value) else { return nil } tokens.remove(at: index) return elements.remove(at: index) } + + /// Perform a binary search on a sorted array returning the index of a value. + /// + /// - parameters: + /// - input: The sorted array to search for `value` + /// - value: The value to find in the sorted `input` array + /// + /// - returns: The index of the `value` or `nil` + private func binarySearch(_ input:ContiguousArray, value: UInt64) -> Int? { + var lower = 0 + var upper = input.count - 1 + + while (true) { + let current = (lower + upper)/2 + if(input[current] == value) { + return current + } + + if (lower > upper) { + return nil + } + + if (input[current] > value) { + upper = current - 1 + } else { + lower = current + 1 + } + } + } } extension Bag: RandomAccessCollection { diff --git a/Sources/Disposable.swift b/Sources/Disposable.swift index 4930c83cf..0614d36ac 100644 --- a/Sources/Disposable.swift +++ b/Sources/Disposable.swift @@ -378,3 +378,19 @@ public final class SerialDisposable: Disposable { state.deinitialize() } } + +extension ScopedDisposable where Inner == SerialDisposable { + /// The current inner disposable of the `SerialDisposable` wrapped + /// in the `ScopedDisposable` to dispose of. + /// + /// Whenever this property is set (even to the same value!), the previous + /// disposable is automatically disposed. + public var inner: Disposable? { + get { + return inner.inner + } + set { + inner.inner = newValue + } + } +}