diff --git a/Demo/Demo.xcodeproj/project.pbxproj b/Demo/Demo.xcodeproj/project.pbxproj index e95ee5f..4012387 100644 --- a/Demo/Demo.xcodeproj/project.pbxproj +++ b/Demo/Demo.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 4782C2FD3F28267D0A736242 /* Pods_DemoTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98ABDE1166974D890C0D32DA /* Pods_DemoTests.framework */; }; - 5E6ACB541F5863D30050E957 /* ProtocolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E6ACB531F5863D30050E957 /* ProtocolTests.swift */; }; 5E9E831F1BF79B3000C85B46 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E9E831E1BF79B3000C85B46 /* AppDelegate.swift */; }; 5E9E83211BF79B3000C85B46 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E9E83201BF79B3000C85B46 /* ViewController.swift */; }; 5E9E83241BF79B3000C85B46 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E9E83221BF79B3000C85B46 /* Main.storyboard */; }; @@ -32,7 +31,6 @@ 1E9B7B66986C52EC97B65AAF /* Pods-Demo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Demo/Pods-Demo.debug.xcconfig"; sourceTree = ""; }; 371C581F5871A4EEFB5D8274 /* Pods-DemoTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DemoTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-DemoTests/Pods-DemoTests.release.xcconfig"; sourceTree = ""; }; 579EF2C5ACCE845BC7962A04 /* Pods-Demo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo.release.xcconfig"; path = "Pods/Target Support Files/Pods-Demo/Pods-Demo.release.xcconfig"; sourceTree = ""; }; - 5E6ACB531F5863D30050E957 /* ProtocolTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProtocolTests.swift; sourceTree = ""; }; 5E9E831B1BF79B3000C85B46 /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 5E9E831E1BF79B3000C85B46 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 5E9E83201BF79B3000C85B46 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -106,7 +104,6 @@ children = ( 5E9E83331BF79B3000C85B46 /* DemoTests.swift */, 5E9E83351BF79B3000C85B46 /* Info.plist */, - 5E6ACB531F5863D30050E957 /* ProtocolTests.swift */, ); path = DemoTests; sourceTree = ""; @@ -355,7 +352,6 @@ buildActionMask = 2147483647; files = ( 5E9E83341BF79B3000C85B46 /* DemoTests.swift in Sources */, - 5E6ACB541F5863D30050E957 /* ProtocolTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Demo/DemoTests/ProtocolTests.swift b/Demo/DemoTests/ProtocolTests.swift deleted file mode 100644 index bb2694d..0000000 --- a/Demo/DemoTests/ProtocolTests.swift +++ /dev/null @@ -1,45 +0,0 @@ -import XCTest -import RxSwift -@testable import NSObject_Rx - -class MockNSObject: NSObject { -} - -class DisposableObject: HasDisposeBag { -} - -class NSObject_RxTests: XCTestCase { - - private var mockNSObject: MockNSObject! - private var disposableObject: DisposableObject! - - override func setUp() { - super.setUp() - self.mockNSObject = MockNSObject() - self.disposableObject = DisposableObject() - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testNSObject() { - XCTAssertNotNil(self.mockNSObject.rx.disposeBag) - let newDisposeBag = DisposeBag() - self.mockNSObject.rx.disposeBag = newDisposeBag - let address1 = Unmanaged.passUnretained(self.mockNSObject.rx.disposeBag as AnyObject).toOpaque() - let address2 = Unmanaged.passUnretained(newDisposeBag as AnyObject).toOpaque() - XCTAssert(address1 == address2) - } - - func testDisposeBagable() { - XCTAssertNotNil(self.disposableObject.disposeBag) - let newDisposeBag = DisposeBag() - self.disposableObject.disposeBag = newDisposeBag - let address1 = Unmanaged.passUnretained(self.disposableObject.disposeBag as AnyObject).toOpaque() - let address2 = Unmanaged.passUnretained(newDisposeBag as AnyObject).toOpaque() - XCTAssert(address1 == address2) - } -} - diff --git a/HasDisposeBag.swift b/HasDisposeBag.swift deleted file mode 100644 index 043bd1e..0000000 --- a/HasDisposeBag.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// DisposeBagable.swift -// NSObject-Rx -// -// Created by Thibault Wittemberg on 2017-08-25. -// Copyright © 2017 RxSwiftCommunity. All rights reserved. -// - -import Foundation -import RxSwift -import ObjectiveC - -fileprivate struct AssociatedKeys { - static var disposeBag = "disposeBag" -} - -/// each DisposeBagable offers a unique Rx DisposeBag instance -public protocol HasDisposeBag { - - /// a unique Rx DisposeBag instance - var disposeBag: DisposeBag { get set } -} - -extension HasDisposeBag { - - private func doLocked(forClosure closure: () -> Void) { - objc_sync_enter(self); defer { objc_sync_exit(self) } - closure() - } - - public var disposeBag: DisposeBag { - get { - var rxDisposeBag: DisposeBag! - doLocked { - let lookup = objc_getAssociatedObject(self, &AssociatedKeys.disposeBag) as? DisposeBag - if let lookup = lookup { - rxDisposeBag = lookup - } else { - let newDisposeBag = DisposeBag() - objc_setAssociatedObject(self, &AssociatedKeys.disposeBag, newDisposeBag, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - rxDisposeBag = newDisposeBag - } - } - return rxDisposeBag - } - - set { - doLocked { - objc_setAssociatedObject(self, &AssociatedKeys.disposeBag, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - } - } -} diff --git a/NSObject+Rx.swift b/NSObject+Rx.swift index ac14a4a..c2d75c6 100644 --- a/NSObject+Rx.swift +++ b/NSObject+Rx.swift @@ -2,17 +2,36 @@ import Foundation import RxSwift import ObjectiveC -extension NSObject: HasDisposeBag { +fileprivate var disposeBagContext: UInt8 = 0 + +extension Reactive where Base: AnyObject { + func synchronizedBag( _ action: () -> T) -> T { + objc_sync_enter(self.base) + let result = action() + objc_sync_exit(self.base) + return result + } } -public extension Reactive where Base: HasDisposeBag { +public extension Reactive where Base: AnyObject { - /// a unique DisposeBag that is related to the Reactive.Base instance - var disposeBag: DisposeBag { - get { return base.disposeBag } + /// a unique DisposeBag that is related to the Reactive.Base instance only for Reference type + public var disposeBag: DisposeBag { + get { + return synchronizedBag { + if let disposeObject = objc_getAssociatedObject(base, &disposeBagContext) as? DisposeBag { + return disposeObject + } + let disposeObject = DisposeBag() + objc_setAssociatedObject(base, &disposeBagContext, disposeObject, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + return disposeObject + } + } + set { - var mutableBase = base - mutableBase.disposeBag = newValue + synchronizedBag { + objc_setAssociatedObject(base, &disposeBagContext, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } } } }