Skip to content

Commit ea5fd45

Browse files
authored
Merge pull request #487 from ReactiveCocoa/event-transforming
Cutting the SignalProducer overhead further.
2 parents f95b2e4 + 748b4c0 commit ea5fd45

File tree

7 files changed

+386
-148
lines changed

7 files changed

+386
-148
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# master
22
*Please add new entries at the top.*
33

4+
1. The `SignalProducer` internals have undergone a significant refactoring, which bootstraps the effort to reduce the overhead of constant producers and producer compositions. (#487, kudos to @andersio)
5+
46
# 2.0.1
57
1. Addressed the exceptionally high build time. (#495)
68

Sources/Atomic.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ internal struct UnsafeAtomicState<State: RawRepresentable> where State.RawValue
4242
/// - returns: `true` if the current state matches the expected state.
4343
/// `false` otherwise.
4444
internal func `is`(_ expected: State) -> Bool {
45-
return OSAtomicCompareAndSwap32Barrier(expected.rawValue,
46-
expected.rawValue,
47-
value)
45+
return expected.rawValue == value.pointee
4846
}
4947

5048
/// Try to transition from the expected current state to the specified next
@@ -82,7 +80,7 @@ internal struct UnsafeAtomicState<State: RawRepresentable> where State.RawValue
8280
/// - returns: `true` if the current state matches the expected state.
8381
/// `false` otherwise.
8482
internal func `is`(_ expected: State) -> Bool {
85-
return value.modify { $0 == expected.rawValue }
83+
return value.value == expected.rawValue
8684
}
8785

8886
/// Try to transition from the expected current state to the specified next

Sources/Disposable.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,31 @@ extension UnsafeAtomicState where State == DisposableState {
3838
}
3939
}
4040

41+
/// A disposable that does not have side effect upon disposal.
42+
internal final class _SimpleDisposable: Disposable {
43+
private let state = UnsafeAtomicState<DisposableState>(.active)
44+
45+
var isDisposed: Bool {
46+
return state.is(.disposed)
47+
}
48+
49+
func dispose() {
50+
_ = state.tryDispose()
51+
}
52+
53+
deinit {
54+
state.deinitialize()
55+
}
56+
}
57+
58+
/// A disposable that has already been disposed.
59+
internal final class NopDisposable: Disposable {
60+
static let shared = NopDisposable()
61+
var isDisposed = true
62+
func dispose() {}
63+
private init() {}
64+
}
65+
4166
/// A type-erased disposable that forwards operations to an underlying disposable.
4267
public final class AnyDisposable: Disposable {
4368
private final class ActionDisposable: Disposable {

Sources/Event.swift

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,89 @@ extension Signal.Event: EventProtocol {
178178
return self
179179
}
180180
}
181+
182+
extension Signal.Event {
183+
internal static func filter(_ isIncluded: @escaping (Value) -> Bool) -> (@escaping Signal<Value, Error>.Observer.Action) -> (Signal<Value, Error>.Event) -> Void {
184+
return { action in
185+
return { event in
186+
switch event {
187+
case let .value(value):
188+
if isIncluded(value) {
189+
action(.value(value))
190+
}
191+
192+
case .completed:
193+
action(.completed)
194+
195+
case let .failed(error):
196+
action(.failed(error))
197+
198+
case .interrupted:
199+
action(.interrupted)
200+
}
201+
}
202+
}
203+
}
204+
205+
internal static func filterMap<U>(_ transform: @escaping (Value) -> U?) -> (@escaping Signal<U, Error>.Observer.Action) -> (Signal<Value, Error>.Event) -> Void {
206+
return { action in
207+
return { event in
208+
switch event {
209+
case let .value(value):
210+
if let newValue = transform(value) {
211+
action(.value(newValue))
212+
}
213+
214+
case .completed:
215+
action(.completed)
216+
217+
case let .failed(error):
218+
action(.failed(error))
219+
220+
case .interrupted:
221+
action(.interrupted)
222+
}
223+
}
224+
}
225+
}
226+
227+
internal static func map<U>(_ transform: @escaping (Value) -> U) -> (@escaping Signal<U, Error>.Observer.Action) -> (Signal<Value, Error>.Event) -> Void {
228+
return { action in
229+
return { event in
230+
switch event {
231+
case let .value(value):
232+
action(.value(transform(value)))
233+
234+
case .completed:
235+
action(.completed)
236+
237+
case let .failed(error):
238+
action(.failed(error))
239+
240+
case .interrupted:
241+
action(.interrupted)
242+
}
243+
}
244+
}
245+
}
246+
247+
internal static func mapError<E>(_ transform: @escaping (Error) -> E) -> (@escaping Signal<Value, E>.Observer.Action) -> (Signal<Value, Error>.Event) -> Void {
248+
return { action in
249+
return { event in
250+
switch event {
251+
case let .value(value):
252+
action(.value(value))
253+
254+
case .completed:
255+
action(.completed)
256+
257+
case let .failed(error):
258+
action(.failed(transform(error)))
259+
260+
case .interrupted:
261+
action(.interrupted)
262+
}
263+
}
264+
}
265+
}
266+
}

Sources/Observer.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,24 @@ extension Signal {
1818
/// Whether the observer should send an `interrupted` event as it deinitializes.
1919
private let interruptsOnDeinit: Bool
2020

21+
/// The target observer of `self`.
22+
private let wrapped: AnyObject?
23+
24+
/// An initializer that transforms the action of the given observer with the
25+
/// given transform.
26+
///
27+
/// If the given observer would perform side effect on deinitialization, the
28+
/// created observer would retain it.
29+
///
30+
/// - parameters:
31+
/// - observer: The observer to transform.
32+
/// - transform: The transform.
33+
internal init<U, E: Swift.Error>(_ observer: Signal<U, E>.Observer, _ transform: @escaping (@escaping Signal<U, E>.Observer.Action) -> Action) {
34+
self.action = transform(observer.action)
35+
self.wrapped = observer.interruptsOnDeinit ? observer : nil
36+
self.interruptsOnDeinit = false
37+
}
38+
2139
/// An initializer that accepts a closure accepting an event for the
2240
/// observer.
2341
///
@@ -27,6 +45,7 @@ extension Signal {
2745
/// event as it deinitializes. `false` otherwise.
2846
internal init(action: @escaping Action, interruptsOnDeinit: Bool) {
2947
self.action = action
48+
self.wrapped = nil
3049
self.interruptsOnDeinit = interruptsOnDeinit
3150
}
3251

@@ -37,6 +56,7 @@ extension Signal {
3756
/// - action: A closure to lift over received event.
3857
public init(_ action: @escaping Action) {
3958
self.action = action
59+
self.wrapped = nil
4060
self.interruptsOnDeinit = false
4161
}
4262

Sources/Signal.swift

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,22 @@ extension Signal where Error == NoError {
527527
}
528528

529529
extension Signal {
530+
/// Perform an action upon every event from `self`. The action may generate zero or
531+
/// more events.
532+
///
533+
/// - precondition: The action must be synchronous.
534+
///
535+
/// - parameters:
536+
/// - transform: A closure that creates the said action from the given event
537+
/// closure.
538+
///
539+
/// - returns: A signal that forwards events yielded by the action.
540+
internal func flatMapEvent<U, E>(_ transform: @escaping (@escaping Signal<U, E>.Observer.Action) -> (Event) -> Void) -> Signal<U, E> {
541+
return Signal<U, E> { observer in
542+
return self.observe(.init(observer, transform))
543+
}
544+
}
545+
530546
/// Map each value in the signal to a new value.
531547
///
532548
/// - parameters:
@@ -535,11 +551,7 @@ extension Signal {
535551
///
536552
/// - returns: A signal that will send new values.
537553
public func map<U>(_ transform: @escaping (Value) -> U) -> Signal<U, Error> {
538-
return Signal<U, Error> { observer in
539-
return self.observe { event in
540-
observer.action(event.map(transform))
541-
}
542-
}
554+
return flatMapEvent(Signal.Event.map(transform))
543555
}
544556

545557
#if swift(>=3.2)
@@ -562,11 +574,7 @@ extension Signal {
562574
///
563575
/// - returns: A signal that will send new type of errors.
564576
public func mapError<F>(_ transform: @escaping (Error) -> F) -> Signal<Value, F> {
565-
return Signal<Value, F> { observer in
566-
return self.observe { event in
567-
observer.action(event.mapError(transform))
568-
}
569-
}
577+
return flatMapEvent(Signal.Event.mapError(transform))
570578
}
571579

572580
/// Maps each value in the signal to a new value, lazily evaluating the
@@ -599,18 +607,7 @@ extension Signal {
599607
///
600608
/// - returns: A signal that forwards the values passing the given closure.
601609
public func filter(_ isIncluded: @escaping (Value) -> Bool) -> Signal<Value, Error> {
602-
return Signal { observer in
603-
return self.observe { (event: Event) -> Void in
604-
guard let value = event.value else {
605-
observer.action(event)
606-
return
607-
}
608-
609-
if isIncluded(value) {
610-
observer.send(value: value)
611-
}
612-
}
613-
}
610+
return flatMapEvent(Signal.Event.filter(isIncluded))
614611
}
615612

616613
/// Applies `transform` to values from `signal` and forwards values with non `nil` results unwrapped.
@@ -620,22 +617,7 @@ extension Signal {
620617
///
621618
/// - returns: A signal that will send new values, that are non `nil` after the transformation.
622619
public func filterMap<U>(_ transform: @escaping (Value) -> U?) -> Signal<U, Error> {
623-
return Signal<U, Error> { observer in
624-
return self.observe { (event: Event) -> Void in
625-
switch event {
626-
case let .value(value):
627-
if let mapped = transform(value) {
628-
observer.send(value: mapped)
629-
}
630-
case let .failed(error):
631-
observer.send(error: error)
632-
case .completed:
633-
observer.sendCompleted()
634-
case .interrupted:
635-
observer.sendInterrupted()
636-
}
637-
}
638-
}
620+
return flatMapEvent(Signal.Event.filterMap(transform))
639621
}
640622
}
641623

0 commit comments

Comments
 (0)