I am writing a hiredis binding to Swift and working on the async API part.
I would like to have something similar to EventEmitter in Node.js.
objectToBeListened.on('event', (data) => { ... })
objectToBeListened.emit('event')
Namely I hope only one "on" and one "emit" function for every class I have.
I currently use enum for all event types and switch in "on" function. An extra struct which stores all callback functions is introduced.
I could not implement an universal "emit" function: I just glanced the Generics part of Swift. But is it ever possible? It seems that Swift doesn't have variadic template.
Anyway, my prototype code is really ugly and hard to maintain. Is there any better way to implement an EventEmitter gracefully?
class EEProto {
var A: Int
var B: Double
typealias EventChangeA = (Int, Int) -> Void
typealias EventChangeB = (Double, Double) -> Void
typealias EventChanged = () -> Void
struct RegisteredEvent {
var eventChangeA: EventChangeA[]
var eventChangeB: EventChangeB[]
var eventChanged: EventChanged[]
}
enum EventType {
case changeA(EventChangeA[])
case changeB(EventChangeB[])
case changed(EventChanged[])
}
var registeredEvents: RegisteredEvent
init (A: Int, B: Double) {
self.A = A
self.B = B
registeredEvents = RegisteredEvent(eventChangeA: [], eventChangeB: [], eventChanged: [])
}
func on (event: EventType) {
switch event {
case .changeA(let events):
registeredEvents.eventChangeA += events
case .changeB(let events):
registeredEvents.eventChangeB += events
case .changed(let events):
registeredEvents.eventChanged += events
default:
assert("unhandled event type | check your code")
break
}
}
func resetEvents (eventType: EventType) {
switch eventType {
case .changeA:
registeredEvents.eventChangeA = []
case .changeB:
registeredEvents.eventChangeA = []
case .changed:
registeredEvents.eventChangeA = []
default:
assert("unhandled event type | check your code")
break
}
}
func setA (newA: Int) {
let oldA = A
A = newA
for cb in registeredEvents.eventChangeA {
cb(oldA, newA)
}
for cb in registeredEvents.eventChanged {
cb()
}
}
func setB (newB: Double) {
let oldB = B
B = newB
for cb in registeredEvents.eventChangeB {
cb(oldB, newB)
}
for cb in registeredEvents.eventChanged {
cb()
}
}
}
var inst = EEProto(A: 10, B: 5.5)
inst.on(EEProto.EventType.changeA([{
println("from \($0) to \($1)")
}]))
inst.on(EEProto.EventType.changeB([{
println("from \($0) to \($1)")
}]))
inst.on(EEProto.EventType.changed([{
println("value changed")
}]))
inst.setA(10)
inst.setB(3.14)
You can use a library like FlexEmit. It works very similar to the EventEmitter in NodeJS.
Basically you define your events as swift types (these can be any struct, enum, class, etc.):
struct EnergyLevelChanged {
let newEnergyLevel: Int
init(to newValue: Int) { newEnergyLevel = newValue }
}
struct MovedTo {
let x, y: Int
}
Then you create an emitter and add event listeners for different types of events you want to listen for:
let eventEmitter = Emitter()
eventEmitter.when { (newLocation: MovedTo) in
print("Moved to coordinates \(newLocation.x):\(newLocation.y)")
}
eventEmitter.when { (event: EnergyLevelChanged) in
print("Changed energy level to", event.newEnergyLevel)
}
And finally you send your events using a simple emit function
eventEmitter.emit(EnergyLevelChanged(to: 60)) // prints "Changed energy level to 60"
eventEmitter.emit(MovedTo(x: 0, y: 0)) // prints "Moved to coordinates 0:0"
Related
I have a local cache where I store the runner's lap info, I need to show if the runner's current lap was better or worse than the current lap, while displaying the current lap information.
data class RunInfo(
val runnerId: String,
val lapTime: Double,
var betterThanLastLap: BETTERTHANLASTLAP
)
enum class BETTERTHANLASTLAP {
NA, YES, NO
}
object RunDB {
private var listOfRunners: MutableList<RunInfo> =
java.util.Collections.synchronizedList(mutableListOf())
private var previousList: MutableList<RunInfo> = mutableListOf()
fun save(runList: MutableList<RunInfo>) {
previousList = listOfRunners.toMutableList()
listOfRunners.clear()
listOfRunners.addAll(runList)
listOfRunners.forEach { runner ->
previousList.forEach { previousLap ->
if (runner.runnerId == previousLap.runnerId) {
runner.betterThanLastLap =
when {
previousLap.lapTime == 0.0 -> BETTERTHANLASTLAP.NA
runner.lapTime >= previousLap.lapTime -> BETTERTHANLASTLAP.YES
else -> BETTERTHANLASTLAP.NO
}
}
}
}
}
}
This seems to do the job, but often I get concurrent modification exception. Is there a better way of solving this problem?
I don't recommend combining mutable lists with read-write var properties. Making it mutable in two different ways creates ambiguity and is error prone. Since you're just clearing and replacing the list contents, I would make it a read-only list and a read-write property.
You need to synchronize the whole function so it can only be executed once at a time.
object RunDB {
private var listOfRunners: List<RunInfo> = listOf()
private var previousList: List<RunInfo> = listOf()
fun save(runList: List<RunInfo>) {
sychronized(this) {
previousList = listOfRunners.toList()
listOfRunners = runList.toList()
listOfRunners.forEach { runner ->
previousList.forEach { previousLap ->
if (runner.runnerId == previousLap.runnerId) {
runner.betterThanLastLap =
when {
previousLap.lapTime == 0.0 -> BETTERTHANLASTLAP.NA
runner.lapTime >= previousLap.lapTime -> BETTERTHANLASTLAP.YES
else -> BETTERTHANLASTLAP.NO
}
}
}
}
}
}
}
It also feels error prone to have a mutable data class in these lists that you're copying and shuffling around. I recommend making it immutable:
data class RunInfo(
val runnerId: String,
val lapTime: Double,
val betterThanLastLap: BETTERTHANLASTLAP
)
object RunDB {
private var listOfRunners: List<RunInfo> = listOf()
private var previousList: List<RunInfo> = listOf()
fun save(runList: List<RunInfo>) {
sychronized(this) {
previousList = listOfRunners.toList()
listOfRunners = runList.map { runner ->
val previousLap = previousList.find { runner.runnerId == previousLap.runnerId }
runner.copy(betterThanLastLap = when {
previousLap == null || previousLap.lapTime == 0.0 -> BETTERTHANLASTLAP.NA
runner.lapTime >= previousLap.lapTime -> BETTERTHANLASTLAP.YES
else -> BETTERTHANLASTLAP.NO
})
}
}
}
}
I tried to send a Map to a compute, but computer is never called. The strange point is if I replace Map with int, it works:
void A()
{
var map=Map();
map["p1"]=90;
D("before compute");
var r1 = await compute(p1, 10);
D("after compute(p1) : $r1");
var r2 = await compute(p2, map);
// code never reaches here!
D("after compute(p2) : $r2");
}
static int p2(Map p)
{
return p["p1"]*10;
}
static int p1(int z)
{
return z*10;
}
output is :
after compute(p1) : 100
Flutter compute methods use Isolates and its only transfer (null, num, bool, double, String) types.
https://api.flutter.dev/flutter/dart-isolate/SendPort/send.html
Just define exact type of Map that "p2" receives as parameter:
static int p2(Map<String,int> p)
{
return p["p1"]*10;
}
Try passing a const parameter:
var r2 = await compute(p2, {"p1":90});
Let's say I have the following struct in Swift:
struct Data {
let old: Double
let new: Double
}
Now I have a class with an array of Data structs:
class MyClass {
var myDataArray: [Data]
}
Now let's say I want to calculate the average of either the old or the new values:
func calculateAverage(oldOrNew: String) -> Double {
var total = 0.0
count = 0
for data in myDataArray {
total += data.oldOrNew
count++
}
return total / Double(count)
}
And then:
let oldAverage = calculateAverage("old")
let newAverage = calculateAverage("new")
But this obviously doesn't work, since oldOrNew is not a member of my struct.
How can I access old or new from "old" or "new" ?
What about this "reflection-less" solution?
struct Data {
let old: Double
let new: Double
func valueByPropertyName(name:String) -> Double {
switch name {
case "old": return old
case "new": return new
default: fatalError("Wrong property name")
}
}
}
Now you can do this
let data = Data(old: 0, new: 1)
data.valueByPropertyName("old") // 0
data.valueByPropertyName("new") // 1
You're looking for key-value-coding (KVC) that is accessing properties by key (path).
Short answer: A struct does not support KVC.
If the struct is not mandatory in your design use a subclass of NSObject there you get KVC and even operators like #avg for free.
class MyData : NSObject {
#objc let old, new: Double
init(old:Double, new:Double) {
self.old = old
self.new = new
}
}
let myDataArray : NSArray = [MyData(old: 1, new: 3), MyData(old:5, new: 9), MyData(old: 12, new: 66)]
let averageOld = myDataArray.value(forKeyPath:"#avg.old")
let averageNew = myDataArray.value(forKeyPath: "#avg.new")
Edit: In Swift 4 a struct does support Swift KVC but the operator #avg is not available
You wouldn't access a struct property by name in Swift any more than you would in C++. You'd provide a block.
Extemporaneous:
func calculateAverage(getter: (Data) -> Double) {
... total += getter(data) ...
}
...
calculateAverage({$0.old})
calculateAverage({$0.new})
Possibly with average {$0.old} being a more natural syntax — the verb isn't really helpful and if you're asserting what it is, not what the computer should do, then omitting the brackets looks fine.
Hi I'm new to go and was trying to figure out how maps work.
I have made up a little test program and can't seem to get it to work.
What I'm doing wrong?
package main
import (
"fmt"
)
type Stats struct {
cnt int
category map[string]Events
}
type Events struct {
cnt int
event map[string]Event
}
type Event struct {
value int64
}
func main() {
stats := new(Stats)
stats.cnt = 33
stats.category["aa"].cnt = 66
stats.category["aa"].event["bb"].value = 99
fmt.Println(stats.cnt, stats.category["aa"].event["bb"].value)
}
There are couple of issues with the code:
Map needs to be initialized using make function. Currently they are nil
Return value from map is non-addressable, this because if map is growing it needs to relocated which will cause memory address to change. Hence we need to extract value explicitly from map to a variable, update it and assigning it back.
Use pointer
I have updated the solution to show both updated it value returned and assigning it back and pointer.
http://play.golang.org/p/lv50AONXyU
package main
import (
"fmt"
)
type Stats struct {
cnt int
category map[string]Events
}
type Events struct {
cnt int
event map[string]*Event
}
type Event struct {
value int64
}
func main() {
stats := new(Stats)
stats.cnt = 33
stats.category = make(map[string]Events)
e, f := stats.category["aa"]
if !f {
e = Events{}
}
e.cnt = 66
e.event = make(map[string]*Event)
stats.category["aa"] = e
stats.category["aa"].event["bb"] = &Event{}
stats.category["aa"].event["bb"].value = 99
fmt.Println(stats)
fmt.Println(stats.cnt, stats.category["aa"].event["bb"].value)
}
Adding this as a different approach to the problem:
type Stats struct {
cnt int
categories map[string]*Events
}
func (s *Stats) Category(n string) (e *Events) {
if s.categories == nil {
s.categories = map[string]*Events{}
}
if e = s.categories[n]; e == nil {
e = &Events{}
s.categories[n] = e
}
return
}
type Events struct {
cnt int
events map[string]*Event
}
func (e *Events) Event(n string) (ev *Event) {
if e.events == nil {
e.events = map[string]*Event{}
}
if ev = e.events[n]; ev == nil {
ev = &Event{}
e.events[n] = ev
}
return
}
type Event struct {
value int64
}
func main() {
var stats Stats
stats.cnt = 33
stats.Category("aa").cnt = 66
stats.Category("aa").Event("bb").value = 99
fmt.Println(stats)
fmt.Println(stats.cnt, stats.Category("aa").Event("bb").value)
}
playground
There are a few issues with your approach.
You aren't initializing you maps. You need to create them first.
Maps return copies of their values. So when you pull out "aa" and modify it, you are getting a copy of "aa", changing it, then throwing it away. You need to put it back in the map, or use pointers.
Here's a working example (non-pointer version) on Play.
Notice the construction of the maps, and the re-assignment back to the map when modifying a value.
package main
import (
"fmt"
)
type Stats struct {
cnt int
category map[string]Events
}
type Events struct {
cnt int
event map[string]Event
}
type Event struct {
value int64
}
func main() {
stats := &Stats{category: map[string]Events{}}
stats.cnt = 33
tmpCat, ok := stats.category["aa"]
if !ok {
tmpCat = Events{event: map[string]Event{}}
}
tmpCat.cnt = 66
tmpEv := tmpCat.event["bb"]
tmpEv.value = 99
tmpCat.event["bb"] = tmpEv
stats.category["aa"] = tmpCat
fmt.Println(stats.cnt, stats.category["aa"].event["bb"].value)
}
in an application where there could be multiple threads running, and not sure about the possibilities if these methods will be accessed under a multhreaded environment or not but to be safe, I've done a test class to demonstrate a situation.
One method has was programmed to be thread safe (please also comment if it's done right) but the rest were not.
In a situation like this, where there is only one single line of code inside remove and add, is it necessary to make them thread safe or is it going to be exaggeration.
import Foundation
class Some {}
class Test {
var dict = [String: Some]()
func has(key: String) -> Bool {
var has = false
dispatch_sync(dispatch_queue_create("has", nil), { [unowned self] in
has = self.dict[key] != nil
})
return has
}
func remove(key: String) -> Some {
var ob = dict[key]
dict[key] = nil
return ob
}
func add(key: String, ob: Some) {
dict[key] = ob
}
}
Edit after comments
class Some {}
class Test {
var dict = [String: Some]()
private let queue: dispatch_queue_t = dispatch_queue_create("has", DISPATCH_QUEUE_CONCURRENT)
func has(key: String) -> Bool {
var has = false
dispatch_sync(queue) {
has = self.dict[key] != nil
}
return has
}
func remove(key: String) -> Some? { //returns
var removed: Some?
dispatch_barrier_sync(queue) {
removed = self.dict.removeValueForKey(key)
}
return removed
}
func add(key: String, ob: Some) { //not async
dispatch_barrier_sync(queue) {
self.dict[key] = ob
}
}
}
The way you are checking whether a key exists is incorrect. You are creating a new queue every time, which means the operations are not happening synchronously.
The way I would do it is like so:
class Some {}
class Test {
var dict = [String: Some]()
private let queue: dispatch_queue_t = dispatch_queue_create("has", DISPATCH_QUEUE_CONCURRENT)
func has(key: String) -> Bool {
var has = false
dispatch_sync(queue) { [weak self] in
guard let strongSelf = self else { return }
has = strongSelf.dict[key] != nil
}
return has
}
func remove(key: String) {
dispatch_barrier_async(queue) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.dict[key] = nil
}
}
func add(key: String, ob: Some) {
dispatch_barrier_async(queue) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.dict[key] = ob
}
}
}
Firstly, I am creating a serial queue that is going to be used to access the dictionary as a property of the object, rather than creating a new one every time. The queue is private as it is only used internally.
When I want to get a value out of the class, I am just dispatching a block synchronously to the queue and waits for the block to finish before returning whether or not the queue exists. Since this is not mutating the dictionary, it is safe for multiple blocks of this sort to run on the concurrent queue.
When I want to add or remove values from the dictionary, I am adding the block to the queue but with a barrier. What this does is that it stops all other blocks on the queue while it is running. When it is finished, all the other blocks can run concurrently. I am using an async dispatch, because I don't need to wait for a return value.
Imagine you have multiple threads trying to see whether or not key values exist or adding or removing values. If you have lots of reads, then they happen concurrently, but when one of the blocks is run that will change the dictionary, all other blocks wait until this change is completed and then start running again.
In this way, you have the speed and convenience of running concurrently when getting values, and the thread safety of blocking while the dictionary is being mutated.
Edited to add
self is marked as weak in the block so that it doesn't create a reference cycle. As #MartinR mentioned in the comments; it is possible that the object is deallocated while blocks are still in the queue, If this happens then self is undefined, and you'll probably get a runtime error trying to access the dictionary, as it may also be deallocated.
By setting declaring self within the block to be weak, if the object exists, then self will not be nil, and can be conditionally unwrapped into strongSelf which points to self and also creates a strong reference, so that self will not be deallocated while the instructions in the block are carried out. When these instructions complete, strongSelf will go out of scope and release the strong reference to self.
This is sometimes known as the "strong self, weak self dance".
Edited Again : Swift 3 version
class Some {}
class Test {
var dict = [String: Some]()
private let queue = DispatchQueue(label: "has", qos: .default, attributes: .concurrent)
func has(key: String) -> Bool {
var has = false
queue.sync { [weak self] in
guard let strongSelf = self else { return }
has = strongSelf.dict[key] != nil
}
return has
}
func remove(key: String) {
queue.async(flags: .barrier) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.dict[key] = nil
}
}
func add(key: String, ob: Some) {
queue.async(flags: .barrier) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.dict[key] = ob
}
}
}
Here is another swift 3 solution which provides thread-safe access to AnyObject.
It allocates recursive pthread_mutex associated with 'object' if needed.
class LatencyManager
{
private var latencies = [String : TimeInterval]()
func set(hostName: String, latency: TimeInterval) {
synchronizedBlock(lockedObject: latencies as AnyObject) { [weak self] in
self?.latencies[hostName] = latency
}
}
/// Provides thread-safe access to given object
private func synchronizedBlock(lockedObject: AnyObject, block: () -> Void) {
objc_sync_enter(lockedObject)
block()
objc_sync_exit(lockedObject)
}
}
Then you can call for example set(hostName: "stackoverflow.com", latency: 1)
UPDATE
You can simply define a method in a swift file (not in a class):
/// Provides thread-safe access to given object
public func synchronizedAccess(to object: AnyObject, _ block: () -> Void)
{
objc_sync_enter(object)
block()
objc_sync_exit(object)
}
And use it like this:
synchronizedAccess(to: myObject) {
myObject.foo()
}