From ab5c60f7cc3d71998cd938e73559c7dc6541d6be Mon Sep 17 00:00:00 2001 From: Thibault Wittemberg Date: Sun, 15 Oct 2017 20:46:46 -0400 Subject: [PATCH] feature: rehabilitates the HasDisposeBag protocol The HasDisposeBag protocol is back, but doest not share anymore code with AnyObject, so there is no collision between self and base references The HasDisposeBag protocol can only be implemented by a class, as it uses objc_setAssociatedObject. It is safer from a memory management point of view. --- Demo/DemoTests/DemoTests.swift | 19 ++++++++++++++- HasDisposeBag.swift | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100755 HasDisposeBag.swift diff --git a/Demo/DemoTests/DemoTests.swift b/Demo/DemoTests/DemoTests.swift index f4a3749..48843fe 100644 --- a/Demo/DemoTests/DemoTests.swift +++ b/Demo/DemoTests/DemoTests.swift @@ -3,31 +3,48 @@ import Nimble import RxSwift import NSObject_Rx +class DisposeBagTest: HasDisposeBag { + +} + class Test: QuickSpec { override func spec() { it("respects setter") { var subject = NSObject() let disposeBag = DisposeBag() subject.rx.disposeBag = disposeBag - + let subjectProtocol = DisposeBagTest() + subjectProtocol.disposeBag = disposeBag expect(subject.rx.disposeBag) === disposeBag + expect(subjectProtocol.disposeBag) === disposeBag } it("diposes when object is deallocated") { var executed = false + var executedProtocol = false + let variable = PublishSubject() + let variableProtocol = PublishSubject() // Force the bag to deinit (and dispose itself). do { let subject = NSObject() + let subjectProtocol = DisposeBagTest() + variable.subscribe(onNext: { _ in executed = true }).addDisposableTo(subject.rx.disposeBag) + + variableProtocol.subscribe(onNext: { _ in + executedProtocol = true + }).addDisposableTo(subjectProtocol.disposeBag) } // Force a new value through the subscription to test its been disposed of. variable.onNext(1) + variableProtocol.onNext(1) expect(executed) == false + expect(executedProtocol) == false } it("disposes using rx.disposeBag") { diff --git a/HasDisposeBag.swift b/HasDisposeBag.swift new file mode 100755 index 0000000..158e09a --- /dev/null +++ b/HasDisposeBag.swift @@ -0,0 +1,44 @@ +import Foundation +import RxSwift +import ObjectiveC + +fileprivate var disposeBagContext: UInt8 = 0 + +/// each HasDisposeBag offers a unique RxSwift DisposeBag instance +public protocol HasDisposeBag: class { + + /// a unique RxSwift DisposeBag instance + var disposeBag: DisposeBag { get set } +} + +extension HasDisposeBag { + + func synchronizedBag( _ action: () -> T) -> T { + objc_sync_enter(self) + let result = action() + objc_sync_exit(self) + return result + } + + public var disposeBag: DisposeBag { + get { + return synchronizedBag { + if let disposeObject = objc_getAssociatedObject(self, &disposeBagContext) as? DisposeBag { + return disposeObject + } + let disposeObject = DisposeBag() + objc_setAssociatedObject(self, &disposeBagContext, disposeObject, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + return disposeObject + } + } + + set { + synchronizedBag { + objc_setAssociatedObject(self, &disposeBagContext, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + } +} + + +