Skip to content

Develop #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions SwiftyEventBus.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
4254026B22F8376D007AAD6A /* EventBusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C9C57120B4177C00E197F7 /* EventBusTests.swift */; };
4254026C22F8376D007AAD6A /* EventBusPriorityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C9C61F20BC270800E197F7 /* EventBusPriorityTests.swift */; };
4254026D22F8376D007AAD6A /* EventBusFeatureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429670FB2111A88E00861796 /* EventBusFeatureTests.swift */; };
427C5B4922FBF6B60091A3C6 /* Bucket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427C5B4822FBF6B60091A3C6 /* Bucket.swift */; };
427C5B4A22FC01630091A3C6 /* Bucket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427C5B4822FBF6B60091A3C6 /* Bucket.swift */; };
427C5B4B22FC01640091A3C6 /* Bucket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427C5B4822FBF6B60091A3C6 /* Bucket.swift */; };
427C5B4C22FC01650091A3C6 /* Bucket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427C5B4822FBF6B60091A3C6 /* Bucket.swift */; };
427C5B6722FC20720091A3C6 /* EventBusLifeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427C5B6622FC20720091A3C6 /* EventBusLifeTests.swift */; };
429670E12110630E00861796 /* EventBusSticky.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429670E02110630E00861796 /* EventBusSticky.swift */; };
429670E22110630E00861796 /* EventBusSticky.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429670E02110630E00861796 /* EventBusSticky.swift */; };
429670E32110630E00861796 /* EventBusSticky.swift in Sources */ = {isa = PBXBuildFile; fileRef = 429670E02110630E00861796 /* EventBusSticky.swift */; };
Expand Down Expand Up @@ -116,6 +121,8 @@
4216EC1D22C463EA0067ABF1 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = "<group>"; };
4216EC1F22C463FB0067ABF1 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = "<group>"; };
4254026322F83437007AAD6A /* SwiftyEventBusRx.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; path = SwiftyEventBusRx.podspec; sourceTree = "<group>"; };
427C5B4822FBF6B60091A3C6 /* Bucket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bucket.swift; sourceTree = "<group>"; };
427C5B6622FC20720091A3C6 /* EventBusLifeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventBusLifeTests.swift; sourceTree = "<group>"; };
429670E02110630E00861796 /* EventBusSticky.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBusSticky.swift; sourceTree = "<group>"; };
429670E42110634200861796 /* EventBusMiddleware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBusMiddleware.swift; sourceTree = "<group>"; };
429670E8211075A100861796 /* EventBusSafety.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBusSafety.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -311,6 +318,7 @@
42C9C57020B4177C00E197F7 /* EventBusRxTests.swift */,
42C9C57120B4177C00E197F7 /* EventBusTests.swift */,
42C9C61F20BC270800E197F7 /* EventBusPriorityTests.swift */,
427C5B6622FC20720091A3C6 /* EventBusLifeTests.swift */,
429670FB2111A88E00861796 /* EventBusFeatureTests.swift */,
42C9C55220B416AE00E197F7 /* Info.plist */,
);
Expand Down Expand Up @@ -338,6 +346,7 @@
42C9C56420B4177000E197F7 /* EventBus.swift */,
42C9C56520B4177000E197F7 /* EventBus+Postable.swift */,
42C9C56620B4177000E197F7 /* EventSubscriber.swift */,
427C5B4822FBF6B60091A3C6 /* Bucket.swift */,
);
path = Core;
sourceTree = "<group>";
Expand Down Expand Up @@ -894,6 +903,7 @@
429670ED2111A24300861796 /* EventBusMiddleware+Post.swift in Sources */,
42C9C56C20B4177000E197F7 /* EventSubscription.swift in Sources */,
42C9C56A20B4177000E197F7 /* EventPresentable.swift in Sources */,
427C5B4922FBF6B60091A3C6 /* Bucket.swift in Sources */,
42C9C56E20B4177000E197F7 /* EventBus+Postable.swift in Sources */,
42C9C56920B4177000E197F7 /* EventBus+Observable.swift in Sources */,
429670E52110634200861796 /* EventBusMiddleware.swift in Sources */,
Expand All @@ -912,6 +922,7 @@
4254026B22F8376D007AAD6A /* EventBusTests.swift in Sources */,
4254026922F8376D007AAD6A /* EventBusDispatchTests.swift in Sources */,
4254026A22F8376D007AAD6A /* EventBusRxTests.swift in Sources */,
427C5B6722FC20720091A3C6 /* EventBusLifeTests.swift in Sources */,
4254026D22F8376D007AAD6A /* EventBusFeatureTests.swift in Sources */,
4254026C22F8376D007AAD6A /* EventBusPriorityTests.swift in Sources */,
);
Expand All @@ -926,6 +937,7 @@
42C9C59E20B6B0D500E197F7 /* EventSubscriber.swift in Sources */,
42C9C59C20B6B0D500E197F7 /* EventBus.swift in Sources */,
42C9C59B20B6B0D500E197F7 /* EventSubscription.swift in Sources */,
427C5B4A22FC01630091A3C6 /* Bucket.swift in Sources */,
429670F82111A63600861796 /* EventBusPureMiddleWare+Observer.swift in Sources */,
42C9C59A20B6B0D500E197F7 /* EventDispatch.swift in Sources */,
42C9C59820B6B0D500E197F7 /* EventBus+Observable.swift in Sources */,
Expand All @@ -946,6 +958,7 @@
429670EF2111A24300861796 /* EventBusMiddleware+Post.swift in Sources */,
42C9C5F120B6B56A00E197F7 /* EventSubscriber.swift in Sources */,
42C9C5EF20B6B56A00E197F7 /* EventBus.swift in Sources */,
427C5B4B22FC01640091A3C6 /* Bucket.swift in Sources */,
42C9C5EE20B6B56A00E197F7 /* EventSubscription.swift in Sources */,
429670E62110634200861796 /* EventBusMiddleware.swift in Sources */,
429670EA211075A100861796 /* EventBusSafety.swift in Sources */,
Expand Down Expand Up @@ -974,6 +987,7 @@
429670F02111A24300861796 /* EventBusMiddleware+Post.swift in Sources */,
42C9C5E820B6B14A00E197F7 /* EventSubscriber.swift in Sources */,
42C9C5E620B6B14A00E197F7 /* EventBus.swift in Sources */,
427C5B4C22FC01650091A3C6 /* Bucket.swift in Sources */,
42C9C5E520B6B14A00E197F7 /* EventSubscription.swift in Sources */,
429670E72110634200861796 /* EventBusMiddleware.swift in Sources */,
429670EB211075A100861796 /* EventBusSafety.swift in Sources */,
Expand Down
152 changes: 152 additions & 0 deletions SwiftyEventBus/Core/Bucket.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
//
// Bucket.swift
// SwiftyEventBus
//
// Created by Maru on 2019/8/8.
// Copyright © 2019 Alloc. All rights reserved.
//

import Foundation

/// The open address width of hash-table
public let ObserverHubOpenWidth = 12

public class ObserverHub {

/// The holder of all buckets
private let buckets = [BucketEntry](repeating: BucketEntry(), count: ObserverHubOpenWidth)

/// Find all observers that observe The type `T`
///
/// - Parameter type: The Event type
/// - Returns: The set of all observers
public func findAll<T>(_ type: T.Type) -> Set<EventSubscriber<T>>? {
let id = EventID(type)
let index = abs(id.hashValue % ObserverHubOpenWidth)
let entry = buckets[index]
objc_sync_enter(entry)
defer {
objc_sync_exit(entry)
}
let bucket: Bucket
if let oldBucket = entry.find(with: id) {
bucket = oldBucket
} else {
bucket = Bucket(eventID: id, entry: entry)
}
return bucket.subscribers as? Set<EventSubscriber<T>>
}

/// Append observer to opaque `ObserverHub`
///
/// - Parameter observer: Any type of subscriber
public func add<T>(_ observer: EventSubscriber<T>) {
let id = EventID(T.self)
let index = abs(id.hashValue % ObserverHubOpenWidth)
let entry = buckets[index]
objc_sync_enter(entry)
defer {
objc_sync_exit(entry)
}
let bucket: Bucket
let isOld: Bool
if let oldBucket = entry.find(with: id) {
bucket = oldBucket
isOld = true
} else {
bucket = Bucket(eventID: id, entry: entry)
isOld = false
}
/// note: don't use append method, it will waste mem-space.
if var subscribers = bucket.subscribers as? Set<EventSubscriber<T>> {
subscribers.insert(observer)
bucket.subscribers = subscribers
} else {
bucket.subscribers = Set<EventSubscriber<T>>(arrayLiteral: observer)
}
if entry.head == nil {
entry.head = bucket
}
if (!isOld) {
bucket.parent = entry.trail
entry.trail?.next = bucket
entry.trail = bucket
}
}

/// Remove observer from opaque `ObserverHub`
///
/// - Parameter observer: Any type of subscriber
public func remove<T>(_ observer: EventSubscriber<T>) {
let id = EventID(T.self)
let index = abs(id.hashValue % ObserverHubOpenWidth)
let entry = buckets[index]
objc_sync_enter(entry)
defer {
objc_sync_exit(entry)
}
guard let bucket = entry.find(with: id) else {
return
}
guard var subscribers = bucket.subscribers as? Set<EventSubscriber<T>> else {
return
}
subscribers.remove(observer)
if subscribers.isEmpty {
// remove bucket node
let nextBucket = bucket.next
if (nextBucket == nil) {
entry.trail = bucket.parent
}
if (bucket.parent == nil) {
entry.head = nil
}
bucket.parent?.next = nextBucket
bucket.parent = nil
bucket.next = nil
} else {
bucket.subscribers = subscribers
}
}
}

class BucketEntry {

var head: Bucket?

var trail: Bucket?

/// find the bucket with specify EventID
///
/// - Parameter id: EventID of type `T`
/// - Returns: bucket match the EventID or nil
func find(with id: String) -> Bucket? {
var bucket = head
while let cur = bucket {
if cur.id == id {
return cur
}
bucket = bucket?.next
}
return bucket
}
}

class Bucket {

/// The EventID of type `T`
let id: String
/// The refrence to root bucket
let entry: BucketEntry
/// All subscribers of this bucket, the real type is `Set<EventSubscriber<T>>`
var subscribers: Any?
/// The refrence to next bucket
var next: Bucket?
/// The refrence to previous bucket
var parent: Bucket?

init(eventID: String, entry: BucketEntry) {
self.id = eventID
self.entry = entry
}
}
5 changes: 2 additions & 3 deletions SwiftyEventBus/Core/EventBus.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ public class EventBus {
/// The domain string that identify different `EventBus` instance
public let domain: String

/// The Dictionary that contain all `EventSubscriber`
/// The observer hub that manage all of observers.
/// Discuss: we use string of type as key, and a set of `EventSubscriber`
/// as value. when register or unregister, we just need to modify corresponding
/// set.
public var observers: [String: Any]
public let observerHub = ObserverHub()

/// The dictinary that contain the lastest N message event
/// for support sticky and replay event
Expand All @@ -60,7 +60,6 @@ public class EventBus {
/// - Parameter domain: The domain string
public init(domain: String) {
self.domain = domain
self.observers = [String: Any]()
}
}

Expand Down
6 changes: 1 addition & 5 deletions SwiftyEventBus/Core/EventSubscription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ public class EventSubscription<T> {
defer {
objc_sync_exit(self)
}
let identifier = EventID(T.self)
if var set = eventBus.observers[identifier] as? Set<EventSubscriber<T>> {
set.remove(entity)
eventBus.observers[identifier] = set
}
eventBus.observerHub.remove(entity)
}
}
8 changes: 2 additions & 6 deletions SwiftyEventBus/Core/Features/EventBusMiddleware+Post.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ import Foundation
extension EventBusPureMiddleWare: EventBusPostable {

public func post<T>(_ cargo: T) {
let identifier = EventID(T.self)
if (support(.sticky)) {
host.replayBuff.enqueue(cargo)
}
guard let queue = host.observers[identifier] as? Set<EventSubscriber<T>> else {
guard let queue = host.observerHub.findAll(T.self) else {
return
}
performPost(with: queue, cargo: cargo)
Expand All @@ -24,13 +23,10 @@ extension EventBusPureMiddleWare: EventBusPostable {
extension EventBusDangerMiddleWare: EventBusSafePostable {

public func post<T>(_ cargo: T) throws {
let identifier = EventID(T.self)
if (support(.sticky)) {
host.replayBuff.enqueue(cargo)
}
guard let queue = host.observers[identifier] as? Set<EventSubscriber<T>> else {
return
}
let queue = host.observerHub.findAll(T.self) ?? []
if (support(.safety) && queue.isEmpty) {
throw EventBusPostError.useless
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,14 @@ import Foundation
extension EventBusMiddleWare: EventBusObservable {

public func register<T>(on mode: DispatchMode = .same, priority: EventBusPriority = .`default`, messageEvent: @escaping (T) -> Void) -> EventSubscription<T> {
let identifier = EventID(T.self)
let subscriber = EventSubscriber(mode: mode, priority: priority, eventHandler: messageEvent)
let subscription = EventSubscription(entity: subscriber, eventBus: host)
if (support(.sticky)) {
if let previousCargo: T = host.replayBuff.dequeue() {
messageEvent(previousCargo)
}
}
if var queue4T = host.observers[identifier] as? Set<EventSubscriber<T>> {
queue4T.insert(subscriber)
host.observers[identifier] = queue4T
} else {
host.observers[identifier] = Set<EventSubscriber<T>>(arrayLiteral: subscriber)
}
host.observerHub.add(subscriber)
return subscription
}
}
6 changes: 1 addition & 5 deletions SwiftyEventBus/Rx/EventSubscription+Rx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ extension EventSubscription: Disposable {
defer {
objc_sync_exit(self)
}
let identifier = EventID(T.self)
if var set = eventBus.observers[identifier] as? Set<EventSubscriber<T>> {
set.remove(entity)
eventBus.observers[identifier] = set
}
eventBus.observerHub.remove(entity)
}
}
28 changes: 28 additions & 0 deletions SwiftyEventBusTests/EventBusLifeTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// EventBusLifeTests.swift
// SwiftyEventBusTests
//
// Created by Maru on 2019/8/8.
// Copyright © 2019 Alloc. All rights reserved.
//

import Foundation
import Quick
import Nimble
@testable import SwiftyEventBus

class EventBusLifeTests: QuickSpec {

override func spec() {
describe("EventBus Life Spec") {
it("will dispose after EventSubscription deinit", closure: {
let bag: EventSubscription<String>? = EventBus.default.register(messageEvent: { (x: String) in
expect(x).to(equal("life"))
})
bag?.dispose()
let observers = EventBus.default.observerHub.findAll(String.self)
expect(observers).to(beNil())
})
}
}
}
10 changes: 6 additions & 4 deletions SwiftyEventBusTests/EventBusPriorityTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class EventBusPriorityTests: QuickSpec {

var box = EventBusBox()

let bus = EventBus(domain: "com.souche.maru.priority")

override func spec() {
describe("EventBus Priority Tests Spec") {
describe("build-in enum usage", {
Expand All @@ -41,7 +43,7 @@ class EventBusPriorityTests: QuickSpec {
let queue = DispatchQueue.global(qos: .utility)
group.enter()
queue.sync(execute: {
EventBus.default.register(priority: .low, messageEvent: { (x: String) in
self.bus.register(priority: .low, messageEvent: { (x: String) in
expect(x).to(equal("foo"))
expect(lowFlag).to(equal(false))
expect(defaultFlag).to(equal(false))
Expand All @@ -52,7 +54,7 @@ class EventBusPriorityTests: QuickSpec {
})
group.enter()
queue.sync {
EventBus.default.register(priority: .`default`, messageEvent: { (x: String) in
self.bus.register(priority: .`default`, messageEvent: { (x: String) in
expect(x).to(equal("foo"))
expect(lowFlag).to(equal(true))
expect(defaultFlag).to(equal(false))
Expand All @@ -63,7 +65,7 @@ class EventBusPriorityTests: QuickSpec {
}
group.enter()
queue.sync {
EventBus.default.register(priority: .high, messageEvent: { (x: String) in
self.bus.register(priority: .high, messageEvent: { (x: String) in
expect(x).to(equal("foo"))
expect(lowFlag).to(equal(true))
expect(defaultFlag).to(equal(true))
Expand All @@ -72,7 +74,7 @@ class EventBusPriorityTests: QuickSpec {
group.leave()
}).release(by: self.box)
}
EventBus.default.post("foo")
self.bus.post("foo")
group.notify(qos: .default, flags: .inheritQoS, queue: DispatchQueue.main, execute: {
expect(lowFlag).to(equal(true))
expect(defaultFlag).to(equal(true))
Expand Down
Loading