Skip to content

Commit

Permalink
PoC: Noncopyable DisposeBag
Browse files Browse the repository at this point in the history
  • Loading branch information
nikolaykasyanov committed Oct 11, 2024
1 parent 7570d44 commit 7e5172e
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 42 deletions.
54 changes: 33 additions & 21 deletions RxSwift/Disposables/DisposeBag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ extension Disposable {
/// Adds `self` to `bag`
///
/// - parameter bag: `DisposeBag` to add `self` to.
public func disposed(by bag: DisposeBag) {
public func disposed(by bag: inout DisposeBag) {
bag.insert(self)
}
}
Expand All @@ -27,27 +27,39 @@ or create a new one in its place.

In case explicit disposal is necessary, there is also `CompositeDisposable`.
*/
public final class DisposeBag: DisposeBase {

private var lock = SpinLock()

// state
private var disposables = [Disposable]()
private var isDisposed = false

public struct DisposeBag: ~Copyable {
private let implementation: _DisposeBag

/// Constructs new empty dispose bag.
public override init() {
super.init()
public init() {
self.implementation = _DisposeBag()
}

/// Adds `disposable` to be disposed when dispose bag is being deinited.
///
/// - parameter disposable: Disposable to add.
public /*mutating*/ func insert(_ disposable: Disposable) {
implementation._insert(disposable)?.dispose()
}
}

private final class _DisposeBag: DisposeBase {

fileprivate var lock = SpinLock()

// state
fileprivate var disposables = [Disposable]()
fileprivate private(set) var isDisposed = false

public override init() {
super.init()
}

public func insert(_ disposable: Disposable) {
self._insert(disposable)?.dispose()
}

private func _insert(_ disposable: Disposable) -> Disposable? {
fileprivate func _insert(_ disposable: Disposable) -> Disposable? {
self.lock.performLocked {
if self.isDisposed {
return disposable
Expand Down Expand Up @@ -86,21 +98,21 @@ public final class DisposeBag: DisposeBase {

extension DisposeBag {
/// Convenience init allows a list of disposables to be gathered for disposal.
public convenience init(disposing disposables: Disposable...) {
self.init()
self.disposables += disposables
public init(disposing disposables: Disposable...) {
self = DisposeBag()
self.implementation.disposables += disposables
}

/// Convenience init which utilizes a function builder to let you pass in a list of
/// disposables to make a DisposeBag of.
public convenience init(@DisposableBuilder builder: () -> [Disposable]) {
public init(@DisposableBuilder builder: () -> [Disposable]) {
self.init(disposing: builder())
}

/// Convenience init allows an array of disposables to be gathered for disposal.
public convenience init(disposing disposables: [Disposable]) {
public init(disposing disposables: [Disposable]) {
self.init()
self.disposables += disposables
self.implementation.disposables += disposables
}

/// Convenience function allows a list of disposables to be gathered for disposal.
Expand All @@ -115,11 +127,11 @@ extension DisposeBag {

/// Convenience function allows an array of disposables to be gathered for disposal.
public func insert(_ disposables: [Disposable]) {

Check failure on line 129 in RxSwift/Disposables/DisposeBag.swift

View workflow job for this annotation

GitHub Actions / Xcode 15 (iOS)

usage of a noncopyable type that compiler can't verify. This is a compiler bug. Please file a bug with a small example of the bug

Check failure on line 129 in RxSwift/Disposables/DisposeBag.swift

View workflow job for this annotation

GitHub Actions / Xcode 15 (iOS)

usage of a noncopyable type that compiler can't verify. This is a compiler bug. Please file a bug with a small example of the bug

Check failure on line 129 in RxSwift/Disposables/DisposeBag.swift

View workflow job for this annotation

GitHub Actions / Xcode 15 (iOS-Example)

usage of a noncopyable type that compiler can't verify. This is a compiler bug. Please file a bug with a small example of the bug

Check failure on line 129 in RxSwift/Disposables/DisposeBag.swift

View workflow job for this annotation

GitHub Actions / Xcode 15 (iOS-Example)

usage of a noncopyable type that compiler can't verify. This is a compiler bug. Please file a bug with a small example of the bug

Check failure on line 129 in RxSwift/Disposables/DisposeBag.swift

View workflow job for this annotation

GitHub Actions / Xcode 15 (Unix)

usage of a noncopyable type that compiler can't verify. This is a compiler bug. Please file a bug with a small example of the bug

Check failure on line 129 in RxSwift/Disposables/DisposeBag.swift

View workflow job for this annotation

GitHub Actions / Xcode 15 (Unix)

usage of a noncopyable type that compiler can't verify. This is a compiler bug. Please file a bug with a small example of the bug

Check failure on line 129 in RxSwift/Disposables/DisposeBag.swift

View workflow job for this annotation

GitHub Actions / Xcode 15 (Unix)

usage of a noncopyable type that compiler can't verify. This is a compiler bug. Please file a bug with a small example of the bug

Check failure on line 129 in RxSwift/Disposables/DisposeBag.swift

View workflow job for this annotation

GitHub Actions / Xcode 15 (Unix)

usage of a noncopyable type that compiler can't verify. This is a compiler bug. Please file a bug with a small example of the bug

Check failure on line 129 in RxSwift/Disposables/DisposeBag.swift

View workflow job for this annotation

GitHub Actions / Xcode 15 (watchOS)

usage of a noncopyable type that compiler can't verify. This is a compiler bug. Please file a bug with a small example of the bug

Check failure on line 129 in RxSwift/Disposables/DisposeBag.swift

View workflow job for this annotation

GitHub Actions / Xcode 15 (watchOS)

usage of a noncopyable type that compiler can't verify. This is a compiler bug. Please file a bug with a small example of the bug

Check failure on line 129 in RxSwift/Disposables/DisposeBag.swift

View workflow job for this annotation

GitHub Actions / Xcode 15 (tvOS)

usage of a noncopyable type that compiler can't verify. This is a compiler bug. Please file a bug with a small example of the bug

Check failure on line 129 in RxSwift/Disposables/DisposeBag.swift

View workflow job for this annotation

GitHub Actions / Xcode 15 (tvOS)

usage of a noncopyable type that compiler can't verify. This is a compiler bug. Please file a bug with a small example of the bug
self.lock.performLocked {
if self.isDisposed {
self.implementation.lock.performLocked {
if self.implementation.isDisposed {
disposables.forEach { $0.dispose() }
} else {
self.disposables += disposables
self.implementation.disposables += disposables
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions Tests/RxCocoaTests/DelegateProxyTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -786,8 +786,9 @@ extension DelegateProxyTest {
let collection = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
let mockDelegate = MockDelegate()
collection.delegate = mockDelegate
collection.rx.contentOffset.subscribe().disposed(by: DisposeBag())

var disposeBag = DisposeBag()
collection.rx.contentOffset.subscribe().disposed(by: &disposeBag)

let selector = #selector(getter: MockDelegate.demoText)
if ((collection.delegate?.responds(to: selector)) != nil) {
let performResult = collection.delegate?.perform(selector)?.takeRetainedValue()
Expand Down
8 changes: 4 additions & 4 deletions Tests/RxCocoaTests/Driver+Test.swift
Original file line number Diff line number Diff line change
Expand Up @@ -269,19 +269,19 @@ extension DriverTest {

relay.asDriver()
.drive(observer)
.disposed(by: disposeBag)
.disposed(by: &disposeBag)

prepareSampleDriver(with: "first")
.drive(relay)
.disposed(by: disposeBag)
.disposed(by: &disposeBag)

prepareSampleDriver(with: "second")
.drive(relay)
.disposed(by: disposeBag)
.disposed(by: &disposeBag)

Observable.just("third")
.bind(to: relay)
.disposed(by: disposeBag)
.disposed(by: &disposeBag)

disposeBag = DisposeBag()

Expand Down
6 changes: 3 additions & 3 deletions Tests/RxCocoaTests/KVOObservableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ final class Parent : NSObject {

self.rx.observe(String.self, "val", options: [.initial, .new], retainSelf: false)
.subscribe(onNext: callback)
.disposed(by: disposeBag)
.disposed(by: &disposeBag)
}

deinit {
Expand All @@ -48,13 +48,13 @@ final class Parent : NSObject {
}

final class Child : NSObject {
let disposeBag = DisposeBag()
var disposeBag = DisposeBag()

init(parent: ParentWithChild, callback: @escaping (String?) -> Void) {
super.init()
parent.rx.observe(String.self, "val", options: [.initial, .new], retainSelf: false)
.subscribe(onNext: callback)
.disposed(by: disposeBag)
.disposed(by: &disposeBag)
}

deinit {
Expand Down
4 changes: 2 additions & 2 deletions Tests/RxCocoaTests/RxTest+Controls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,14 @@ extension RxTest {
func ensureControlObserverHasWeakReference<C, T>(file: StaticString = #file, line: UInt = #line, _ createControl: @autoclosure() -> (C), _ observerSelector: (C) -> AnyObserver<T>, _ observableSelector: () -> (Observable<T>)) where C: NSObject {
var deallocated = false

let disposeBag = DisposeBag()
var disposeBag = DisposeBag()

autoreleasepool {
let control = createControl()
let propertyObserver = observerSelector(control)
let observable = observableSelector()

observable.bind(to: propertyObserver).disposed(by: disposeBag)
observable.bind(to: propertyObserver).disposed(by: &disposeBag)

_ = (control as NSObject).rx.deallocated.subscribe(onNext: { _ in
deallocated = true
Expand Down
16 changes: 8 additions & 8 deletions Tests/RxCocoaTests/SentMessageTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -645,11 +645,11 @@ extension SentMessageTest {
func testFailsInCaseObjectIsAlreadyBeingInterceptedWithKVO_sentMessage() {
let target = SentMessageTest_shared()

let disposeBag = DisposeBag()
var disposeBag = DisposeBag()
target.rx.observe(NSArray.self, "messages")
.subscribe(onNext: { _ in
})
.disposed(by: disposeBag)
.disposed(by: &disposeBag)

do {
_ = try target.rx.sentMessage(#selector(SentMessageTestBase_shared.justCalledBool(toSay:)))
Expand All @@ -672,11 +672,11 @@ extension SentMessageTest {
func testFailsInCaseObjectIsAlreadyBeingInterceptedWithKVO_methodInvoked() {
let target = SentMessageTest_shared()

let disposeBag = DisposeBag()
var disposeBag = DisposeBag()
target.rx.observe(NSArray.self, "messages")
.subscribe(onNext: { _ in
})
.disposed(by: disposeBag)
.disposed(by: &disposeBag)

do {
_ = try target.rx.methodInvoked(#selector(SentMessageTestBase_shared.justCalledBool(toSay:)))
Expand Down Expand Up @@ -863,7 +863,7 @@ extension SentMessageTest {
var messages: Observable<MethodParameters>!
var recordedMessages = [MethodParameters]()
var completed = false
let disposeBag = DisposeBag()
var disposeBag = DisposeBag()

var stages: [MessageProcessingStage] = []

Expand All @@ -883,7 +883,7 @@ extension SentMessageTest {
stages.append(.sentMessage)
}, onCompleted: {
completed = true
}).disposed(by: disposeBag)
}).disposed(by: &disposeBag)

target.justCalledBool(toSay: true)
}
Expand All @@ -897,7 +897,7 @@ extension SentMessageTest {
var messages: Observable<MethodParameters>!
var recordedMessages = [MethodParameters]()
var completed = false
let disposeBag = DisposeBag()
var disposeBag = DisposeBag()

var stages: [MessageProcessingStage] = []

Expand All @@ -917,7 +917,7 @@ extension SentMessageTest {
stages.append(.methodInvoked)
}, onCompleted: {
completed = true
}).disposed(by: disposeBag)
}).disposed(by: &disposeBag)

target.justCalledBool(toSay: true)
}
Expand Down
4 changes: 2 additions & 2 deletions Tests/RxSwiftTests/Anomalies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ extension AnomaliesTest {
}

func test1344(){
let disposeBag = DisposeBag()
var disposeBag = DisposeBag()
let foo = Observable<Int>.create({ observer in
observer.on(.next(1))
Thread.sleep(forTimeInterval: 0.1)
Expand All @@ -119,7 +119,7 @@ extension AnomaliesTest {

Observable.merge(foo, .just([42]))
.subscribe()
.disposed(by: disposeBag)
.disposed(by: &disposeBag)
}

func testSeparationBetweenOnAndSubscriptionLocks() {
Expand Down

0 comments on commit 7e5172e

Please sign in to comment.