Holding and releasing Closures in an array - arrays

I'm looking to make an observable property without relying on a reactive 3rd party lib / framework.
I read this and came up with a similar solution to their Observable Properties answer...
https://blog.scottlogic.com/2015/02/11/swift-kvo-alternatives.html
Theirs
class Observable<T> {
let didChange = Event<(T, T)>()
private var value: T
init(_ initialValue: T) {
value = initialValue
}
func set(newValue: T) {
let oldValue = value
value = newValue
didChange.raise(oldValue, newValue)
}
func get() -> T {
return value
}
}
Mine
public class Observable<V> {
public var value: V { didSet { for observer in observers { observer(value) } }}
private var observers = [(V) -> Void]()
public init(_ initital: V) {
value = initital
}
public func observe(with closure: #escaping (V) -> Void) {
observers.append(closure)
}
}
the only difference is I want to capture an array of closures instead of using Event and addHander... the reason being I want to provide the syntax of passing the value through rather than have the consumers of my code make a function every time and again to not rely on any 3rd party code.
I'm not sure how these closures could automatically be removed from the array once their owners are deallocated. I'm guessing they can't which is why addHandler is used, I'm just hoping someone out there more knowledgable than me can shed some light on the issue.
Thanks for your time.

So I come up with this solution:
class Wrapper<V> {
var observer: (V) -> Void
public init(_ b: #escaping (V) -> Void) {
observer = b
}
}
class Observable<V> {
public var value: V { didSet {
let enumerator = observers.objectEnumerator()
while let wrapper = enumerator?.nextObject() {
(wrapper as! Wrapper<V>).observer(value)
}
}}
private var observers = NSMapTable<AnyObject, Wrapper<V>>(keyOptions: [.weakMemory], valueOptions: [.strongMemory])
public init(_ initital: V) {
value = initital
}
public func observe(_ subscriber: AnyObject, with closure: #escaping (V) -> Void) {
let wrapper = Wrapper(closure)
observers.setObject(wrapper, forKey: subscriber)
}
}
The final API require subscriber to identify themselves at calling:
Observable.observe(self /* <-- extra param */) { /* closure */ }
Although we cannot weak ref a closure, but with NSMapTable, we can weak ref the subscriber object, then use it as a weak key to track observer closure. This allow deallocation of subscriber thus automatically cleanup outdated observers.
Finally, here's the code for a demo. Expand the snippet and copy-paste to swift playground and see it live.
import Foundation
func setTimeout(_ delay: TimeInterval, block:#escaping ()->Void) -> Timer {
return Timer.scheduledTimer(timeInterval: delay, target: BlockOperation(block: block), selector: #selector(Operation.main), userInfo: nil, repeats: false)
}
class Wrapper<V> {
var observer: (V) -> Void
public init(_ b: #escaping (V) -> Void) {
observer = b
}
}
class Observable<V> {
public var value: V { didSet {
let enumerator = observers.objectEnumerator()
while let wrapper = enumerator?.nextObject() {
(wrapper as! Wrapper<V>).observer(value)
}
}}
private var observers = NSMapTable<AnyObject, Wrapper<V>>(keyOptions: [.weakMemory], valueOptions: [.strongMemory])
public init(_ initital: V) {
value = initital
}
public func observe(_ subscriber: AnyObject, with closure: #escaping (V) -> Void) {
let wrapper = Wrapper(closure)
observers.setObject(wrapper, forKey: subscriber)
}
}
class Consumer {
private var id: String
public init(_ id: String, _ observable: Observable<Int>) {
self.id = id
observable.observe(self) { val in
print("[\(id)]", "ok, i see value changed to", val)
}
}
deinit {
print("[\(id)]", "I'm out")
}
}
func demo() -> Any {
let observable = Observable(1)
var list = [AnyObject]()
list.append(Consumer("Alice", observable))
list.append(Consumer("Bob", observable))
observable.value += 1
// pop Bob, so he goes deinit
list.popLast()
// deferred
setTimeout(1.0) {
observable.value += 1
observable.value += 1
}
return [observable, list]
}
// need to hold ref to see the effect
let refHolder = demo()
Edit:
As OP #Magoo commented below, the Wrapper object is not properly deallocated. Even though the subscriber object is successfully deallocated, and corresponding key is removed from NSMapTable, the Wrapper remain active as an entry held in NSMapTable.
Did a bit of test and found this is indeed the case, unexpectedly. Some further research reveal an unfortunate fact: it's a caveat in NSMapTable's implementation.
This post explain the reason behind thoroughly. Quote directly from Apple doc:
However, weak-to-strong NSMapTables are not currently recommended, as the strong values for weak keys which get zero’d out do not get cleared away (and released) until/unless the map table resizes itself.
Hmm, so basically Apple just think it's ok to keep them alive in memory until a resize takes place. Reasonable from GC strategy POV.
Conclusion: no chance it'd be handled if NSMapTables implementation remains the same.
However it shouldn't be a problem for most case. This Observer impl works as intended. And as long as the Wrapper don't do anything fishy and closure don't hold strong ref, only negative impact is just some extra memory footprint.
I do have a fix though, you can use weak -> weak map, so Wrapper as a weak value get dealloc too. But that'll require .observe() returns the Wrapper then Consumer get hold to a ref to it. I'm not keen on this idea, API not ergonomic to end user. I'd rather live with some memory overhead in favor of better API.
Edit 2:
I don't like the aforementioned fix cus the resulting API is not friendly. I saw no way around but #Magoo managed to NAIL IT! Using objc_setAssociatedObject API, which I never heard about before. Make sure to checkout his answer for detail, it's awesome.

OK so #hackape answer with objc_setAssociatedObject
public class Observable<V> {
private class ClosureWrapper<V> {
var closure: (V) -> Void
public init(_ closure: #escaping (V) -> Void) {
self.closure = closure
}
}
private var observers = NSMapTable<AnyObject, ClosureWrapper<V>>(keyOptions: [.weakMemory], valueOptions: [.weakMemory])
public var value: V { didSet { notify() } }
public init(_ initital: V) {
value = initital
}
public func addObserver(_ object: AnyObject, skipFirst: Bool = true, closure: #escaping (V) -> Void) {
let wrapper = ClosureWrapper(closure)
let reference = "observer\(UUID().uuidString)".replacingOccurrences(of: "-", with: "")
observers.setObject(wrapper, forKey: object)
// Giving the closure back to the object that is observing
// allows ClosureWrapper to die at the same time as observing object
objc_setAssociatedObject(object, reference, wrapper, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
if !skipFirst { closure(value) }
}
private func notify() {
let enumerator = observers.objectEnumerator()
while let wrapper = enumerator?.nextObject() { (wrapper as? ClosureWrapper<V>)?.closure(value) }
}
}
This guy also remade the NSMapTable in Swift using a really similar method https://codereview.stackexchange.com/questions/85709/generic-nsmaptable-replacement-written-in-swift

The easiest, and likely safest, solution would be use the exact implementation you have, but make sure all callers use [weak self] and guard that self still exists before performing any actions/side effects.
This way when the array of closures is executed, any that had their creator already dealloc will simply immediately return when called.
// called from outside class
observer.observe { [weak self] in
guard strongSelf = self else { return }
// do work using `strongSelf`
}
If the observer is going to be used by a lot of instances that are constantly deallocating, I'd recommend adding a remove observer function. To do this you'd probably want to return a string on the observe call and then use it for removing the closure. Something along the lines of this:
public typealias ObserverIdentifier = String
public class Observable<V> {
public var value: V { didSet { for observer in observers.values { observer(value) } }}
private var observers = [ObserverIdentifier : (V) -> Void]()
public init(_ initital: V) {
value = initital
}
#discardableResult public func observe(with closure: #escaping (V) -> Void) -> ObserverIdentifier {
let identifier = UUID().uuidString
observers[identifier] = closure
return identifier
}
public func remove(identifier: ObserverIdentifier) {
observers.removeValue(forKey: identifier)
}
}
Because you are using [weak self], removing the observer on deallocation is simply a nice thing to do to avoid some additional no-ops, but still completely safe if not removed.

Related

How to check if two arrays made from different structs have the same value?

I've got two arrays made from different structs and both of them have the same information in different positions of the array.
I've tried making a double for-in loop with an if statement to check that they're the same value, but the way that I do it in other languages like PHP is not working.
Please help me, here's what I've got so far:
Singleton One File
struct oneStruct {
var oneTitle: String
var onePic: UIImage?
var oneCategory: String
}
class OneSingleton {
static let sharedInstance = OneSingleton()
private init() {}
var oneArray: [OneStruct] = [//you get the gist, this is full of array stuff]
}
Singleton Two File
struct TwoStruct {
var twoName: String
var twoPic: UIImage?
var twoSubject: String
var twoStuff1: String
// etc. I can't get into much more here, sorry.
}
class TwoSingleton {
static let sharedInstance = TwoSingleton()
private init() {}
var twoArray: [TwoStruct] = [//you get the gist, this is full of array stuff as well]
}
View Controller
//the singleton stuff is needed
var oneArray = [OneSingleton.sharedInstance.oneArray]
var twoArray = [TwoSingleton.sharedInstance.twoArray]
var filterArray: [TwoStruct] = []
override func viewDidLoad() {
super.viewDidLoad()
for two in twoArray {
for one in oneArray {
if two == one {
filterArray.append(contentsOf: two)
}
}
}
}
It's giving me a compile error
Binary operator '==' cannot be applied to operands of type '[TwoStruct]' and '[OneStruct]'
I hope this all makes sense.
Thanks in advance.
To compare implement Equatable for both and change the lhs or rhs to the other object
struct OneStruct :Equatable {
var value1: String
var value2: UIImage? /// image comparsion is not an easy way you may add id of image
var value3: String
// also you can omit any static method part from any class
static func ==(lhs: TwoStruct, rhs: OneStruct) -> Bool {
return lhs.value1 == rhs.value1 && lhs.value3 == rhs.value3
}
}
struct TwoStruct :Equatable {
var value1: String
var value2: UIImage? /// image comparsion is not an easy way you may add id of image
var value3: String
// also you can omit any static method part from any class
static func ==(lhs: OneStruct, rhs: TwoStruct) -> Bool {
return lhs.value1 == rhs.value1 && lhs.value3 == rhs.value3
}
}
//
let s1 = OneStruct(value1: "", value2: UIImage.init(named: ""), value3: "")
let s2 = TwoStruct(value1: "", value2: UIImage.init(named: ""), value3: "")
if s1 == s2 {
}
It seems you have, for each of your types OneStruct and TwoStruct, one respective property that you would like to use for comparison; category and subject, respectively. So to start out, lets blueprint two protocols for these two properties:
protocol Categorizable {
var category: String { get }
}
protocol Subjectable {
var subject: String { get }
}
For the purpose of this example, minimal examples of OneStruct and TwoStruct would then conform to Categorizable and Subjectable, respectively:
struct OneStruct: Categorizable {
var category: String
init(_ category: String) { self.category = category }
}
struct TwoStruct: Subjectable {
var subject: String
init(_ subject: String) { self.subject = subject }
}
Now, we don't really want to talk about equality when comparing two different types, rather equivalent classes or bi-predicates over which instances of these two types can be compared. So let's define e.g. a bi-predicate that can be used to compare two instances conforming to Categorizable and Subjectable, respectively:
struct BiPredicate<T: Categorizable, U: Subjectable> {
static func compare(_ lhs: T, _ rhs: U) -> Bool {
return lhs.category == rhs.subject
}
static func compare(_ lhs: U, _ rhs: T) -> Bool {
return compare(rhs, lhs)
}
}
Based on your own example code, it seems that you want two filter an array of TwoStruct (namely, an array of a type conforming to Subjectable) based on whether, for each element in the array, there exist a member in an array of OneStruct (namely, an array of a type conforming to Categorizable) for which the equivalent class or bi-predicate holds true.
/// Returns `lhs` argument filtered based on its BiPredicate intersection
/// with the `rhs` argument.
/// Time complexity: O(n^2)
func filterByIntersect<T: Subjectable, U: Categorizable>(_ lhs: [T], over rhs: [U]) -> [T] {
return lhs.filter { l in rhs.contains { r in BiPredicate.compare(l, r) } }
}
Which we may use as:
let oneArray = [OneStruct("foo"), OneStruct("bar"), OneStruct("baz"), OneStruct("bax")]
let twoArray = [TwoStruct("baxx"), TwoStruct("foo"), TwoStruct("bax")]
let result = filterByIntersect(twoArray, over: oneArray)
for twoElement in result { print(twoElement.subject) } /* foo
bax */
Now, this was mostly a somewhat interesting exercise, but in the end ended up in quite a lot of boilerplate code for something that should be quite a simple task. You could always drop the generics and just go with a simple function for testing a single equivalent class over OneStruct and TwoStruct:
func compareCategoryWithSubject(_ lhs: OneStruct, _ rhs: TwoStruct) -> Bool {
return lhs.category == rhs.subject
}
/// Returns `lhs` argument filtered based on intersection over comparing
/// the `subject` properties of the elements of `lhs` with the `category`
/// properties of the elements of `rhs`.
/// Time complexity: O(n^2)
func filterByIntersect(_ lhs: [TwoStruct], over rhs: [OneStruct]) -> [TwoStruct] {
return lhs.filter { l in rhs.contains { r in
compareCategoryWithSubject(r, l) } }
}
Or, for a possibly even simple and also asymptotically more performant approach, placing all the logic into the filterByIntersect(...) function:
/// Returns `lhs` argument filtered based on intersection over comparing
/// the `subject` properties of the elements of `lhs` with the `category`
/// properties of the elements of `rhs`.
/// Time complexity: O(n) [contains(_:) is O(1) for Set)
func filterByIntersect(_ lhs: [TwoStruct], over rhs: [OneStruct]) -> [TwoStruct] {
let rhsCategories = Set(rhs.map { $0.category })
return lhs.filter { l in rhsCategories.contains(l.subject) }
}
Both the above yielding the same result as above:
let oneArray = [OneStruct("foo"), OneStruct("bar"), OneStruct("baz"), OneStruct("bax")]
let twoArray = [TwoStruct("baxx"), TwoStruct("foo"), TwoStruct("bax")]
let result = filterByIntersect(twoArray, over: oneArray)
for twoElement in result { print(twoElement.subject) } /* foo
bax */

How to avoid a retain cycle when using an array of delegates in Swift [duplicate]

This question already has answers here:
Using as a concrete type conforming to protocol AnyObject is not supported
(6 answers)
Closed 5 years ago.
In one of my classes I use an array of delegates (the class is a singleton). This is causing an retain cycle. I know I can avoid the retain cycle when I use only one delegate by making the delegate weak. But this is not working for my array of delegates.
How can I avoid this retain cycle.
Example:
protocol SomeDelegate: class {
func someFunction()
}
My Class
class SomeClass {
// This class is a singleton!
static let sharedInstance = SomeClass()
var delegates = [SomeDelegate]() // this is causing a retain cycle
weak var delegate: SomeDelegate? // this is ok.
... other code...
}
The problem is that weakDelegates is a strong reference and its reference to its elements of type WeakDelegateContainer is a strong reference.
Your situation is why the class NSHashTable exists. Initialize using weakObjects(). This will give you a set of ARC-weak references, each of which will be nilified and removed when the referenced object goes out of existence (with no need for any extra bookkeeping on your part, and no need for your WeakDelegateContainer type).
Your set will have to be typed as holding AnyObject, but you can easily mediate to ensure that you are supplying and retrieving SomeDelegate-conformant objects:
let list = NSHashTable<AnyObject>.weakObjects()
func addToList(_ obj:SomeDelegate) {
list.add(obj)
}
func retrieveFromList(_ obj:SomeDelegate) -> SomeDelegate? {
if let result = list.member(obj) as? SomeDelegate {
return result
}
return nil
}
func retrieveAllFromList() -> [SomeDelegate] {
return list.allObjects as! [SomeDelegate]
}
The function retrieveAllFromList() lists only objects that still exist. Any object that has gone out existence has been changed to nil in the NSHashTable and is not included in allObjects. That is what I mean by "no extra bookkeeping"; the NSHashTable has already done the bookkeeping.
Here is code that tests it:
func test() {
let c = SomeClass() // adopter of SomeDelegate
self.addToList(c)
if let cc = self.retrieveFromList(c) {
cc.someFunction()
}
print(self.retrieveAllFromList()) // one SomeClass object
delay(1) {
print(self.retrieveAllFromList()) // empty
}
}
Alternatively, you can use NSPointerArray. Its elements are pointer-to-void, which can be a little verbose to use in Swift, but you only have to write your accessor functions once (credit to https://stackoverflow.com/a/33310021/341994):
let parr = NSPointerArray.weakObjects()
func addToArray(_ obj:SomeDelegate) {
let ptr = Unmanaged<AnyObject>.passUnretained(obj).toOpaque()
self.parr.addPointer(ptr)
}
func fetchFromArray(at ix:Int) -> SomeDelegate? {
if let ptr = self.parr.pointer(at:ix) {
let obj = Unmanaged<AnyObject>.fromOpaque(ptr).takeUnretainedValue()
if let del = obj as? SomeDelegate {
return del
}
}
return nil
}
Here is code to test it:
let c = SomeClass()
self.addToArray(c)
for ix in 0..<self.parr.count {
if let del = self.fetchFromArray(at:ix) {
del.someFunction() // called
}
}
delay(1) {
print(self.parr.count) // 1
for ix in 0..<self.parr.count {
if let del = self.fetchFromArray(at:ix) {
del.someFunction() // not called
}
}
}
Interestingly, after our SomeClass goes out of existence, our array's count remains at 1 — but cycling through it to call someFunction, there is no call to someFunction. That is because the SomeClass pointer in the array has been replaced by nil. Unlike NSHashTable, the array is not automatically purged of its nil elements. They do no harm, because our accessor code has guarded against error, but if you would like to compact the array, here's a trick for doing it (https://stackoverflow.com/a/40274426/341994):
self.parr.addPointer(nil)
self.parr.compact()
I found the solution in Using as a concrete type conforming to protocol AnyObject is not supported. All credits to Kyle Redfearn.
My solution
protocol SomeDelegate: class {
func someFunction()
}
class WeakDelegateContainer : AnyObject {
weak var weakDelegate: SomeDelegate?
}
class SomeClass {
// This class is a singleton!
static let sharedInstance = SomeClass()
fileprivate var weakDelegates = [WeakDelegateContainer]()
func addDelegate(_ newDelegate: SomeDelegate) {
let container = WeakDelegateContainer()
container.weakDelegate = newDelegate
weakDelegates.append(container)
}
func removeDelegate(_ delegateToRemove: SomeDelegate) {
// In my case: SomeDelegate will always be of the type UIViewController
if let vcDelegateToRemove = delegateToRemove as? UIViewController {
for i in (0...weakDelegates.count - 1).reversed() {
if weakDelegates[i].weakDelegate == nil {
// object that is referenced no longer exists
weakDelegates.remove(at: i)
continue
}
if let vcDelegate = weakDelegates[i].weakDelegate as? UIViewController {
if vcDelegate === vcDelegateToRemove {
weakDelegates.remove(at: i)
}
}
}
}
}
... other code ...
}

How to check if an object is in array

Edit: The problem is already solved by #vacawama. But if you are looking for an answer for NSObject classes, you should implement isEqual function which is NSObjectProtocol. Otherwise you gonna get an error says: " Redundant conformance of 'classname' to protocol 'Equatable' "
You can check this for details: Swift 2.2, Contains Method not working
In swift, how can i check if an object is in array?
I have a simple class like this;
class Test: {
private var _number: Int!
private var _type: String!
var number: Int {
return _number
}
var type: String {
return _type
}
init (number: Int, type: String) {
self._number = number
self._type = type
}
}
Also i have this class;
class TestRandom {
private let _numberArr: [Int] = [1,2,3,4,5,6,7,8,9,10]
private let _typeArr: [String] = ["x","y","z"]
public private(set) var _testArr: [Test] = []
private var _randomTest: Test!
func randomTestPicker () {
repeat {
let randomNumber = Int(arc4random_uniform(UInt32(self._numberArr.count)))
let randomType = Int(arc4random_uniform(UInt32(self._typeArr.count)))
self._randomTest = Test(number: self._numberArr[randomNumber], type: self._typeArr[randomType])
} while self._testArr.contains(_randomTest)
}
}
All i want to do is to pick different objects. Lets say i have x2,y4,x6,z3,z8,y2 in _testArr. When i call randomTestPicker, it should not pick x2 or z8. Because they are already in array.
I have tried contains as you see. However it did not work for me. Is there any solution that i can use for this purpose? Or what is the best way to do this?
Edit: I tried self._testArr.contains{$0 === _randomTest} but not working neither.
You can't use contains that way since your class doesn't conform to the Equatable protocol.
Add :Equatable to your class definition and implement the == function which compares two of your objects:
class Test: Equatable {
private var _number: Int!
private var _type: String!
var number: Int {
return _number
}
var type: String {
return _type
}
init (number: Int, type: String) {
self._number = number
self._type = type
}
}
func ==(lhs: Test, rhs: Test) -> Bool {
return lhs.number == rhs.number && lhs.type == rhs.type
}
The other way this could have been done is to use the predicate form of contains. The predicate takes two objects and returns a Bool indicating if they match. In that case, you would write:
self._testArr.contains { $0.number == _randomTest.number && $0.type == _randomTest.type }
As you can see, in this case the closure is essentially the == function from above, so implementing the Equatable protocol is the cleaner way to do it.
The closure { $0 === _randomTest } doesn't work because that only tests if the objects are the same instance. In your case, you need to check if the two objects have the same properties, and you are not interested if they are same instance. The way you are creating the objects, you never would create an instance that is already in the array, so this check would always return false.

How do I cast the argument type within a closure signature in Swift?

I'm trying to write a light observer class in Swift (currently Swift 2). The idea is to use it within an Entity Component system, as a means for the components to communicate with one-another without being coupled together.
The problem I'm having is that all types of data could be communicated, a CGVector, an NSTimeInterval and so on. This means that the method being passed could have all kinds of type signatures (CGVector) -> Void, () -> Void etc.
I'd like to be able to store these varying signatures in an array, but still have some type safety. My thinking is that the type for the array would be (Any) -> Void or perhaps (Any?) -> Void, so that I can at least ensure that it contains methods. But I'm having trouble passing methods in this way: Cannot convert value of type '(CGVector) -> ()' to expected argument type '(Any) -> ()'.
First attempt:
//: Playground - noun: a place where people can play
import Cocoa
import Foundation
enum EventName: String {
case input, update
}
struct Binding{
let listener: Component
let action: (Any) -> ()
}
class EventManager {
var events = [EventName: [Binding]]()
func add(name: EventName, event: Binding) {
if var eventArray = events[name] {
eventArray.append(event)
} else {
events[name] = [event]
}
}
func dispatch(name: EventName, argument: Any) {
if let eventArray = events[name] {
for element in eventArray {
element.action(argument)
}
}
}
func remove(name: EventName, listener: Component) {
if var eventArray = events[name] {
eventArray = eventArray.filter(){ $0.listener.doc != listener.doc }
}
}
}
// Usage test
//Components
protocol Component {
var doc: String { get }
}
class Input: Component {
let doc = "InputComponent"
let eventManager: EventManager
init(eventManager: EventManager) {
self.eventManager = eventManager
}
func goRight() {
eventManager.dispatch(.input, argument: CGVector(dx: 10, dy: 0) )
}
}
class Movement: Component {
let doc = "MovementComponent"
func move(vector: CGVector) {
print("moved \(vector)")
}
}
class Physics: Component {
let doc = "PhysicsComponent"
func update(time: NSTimeInterval){
print("updated at \(time)")
}
}
class someClass {
//events
let eventManager = EventManager()
// components
let inputComponent: Input
let moveComponent = Movement()
init() {
inputComponent = Input(eventManager: eventManager)
let inputBinding = Binding(listener: moveComponent, action: moveComponent.move) // ! Cannot convert value of type '(CGVector) -> ()' to expected argument type '(Any) -> ()'
eventManager.add(.input, event: inputBinding)
}
}
let someInstance = someClass()
someInstance.inputComponent.goRight()
Throws the error Cannot convert value of type '(CGVector) -> ()' to expected argument type '(Any) -> ()'.
Second attempt
If I genericize the Binding struct to recognise different types of arguments I have a bit more luck. This version basically works, but the array holding the methods is now [Any] ( I'm not sure whether it's the attempt to cast Any back to the Binding struct that is causing the slightly odd error below Binary operator '!=' cannot be applied to two 'String' operands):
struct Binding<Argument>{
let listener: Component
let action: (Argument) -> ()
}
class EventManager {
var events = [EventName: [Any]]()
func add(name: EventName, event: Any) {
if var eventArray = events[name] {
eventArray.append(event)
} else {
events[name] = [event]
}
}
func dispatch<Argument>(name: EventName, argument: Argument) {
if let eventArray = events[name] {
for element in eventArray {
(element as! Binding<Argument>).action(argument)
}
}
}
func remove(name: EventName, listener: Component) {
if var eventArray = events[name] {
// eventArray = eventArray.filter(){ ($0 as! Binding).listener.doc != listener.doc } //Binary operator '!=' cannot be applied to two 'String' operands
}
}
}
Is there a way to do this and have the array hold methods of varying type signatures, something like [(Any?) -> ()] ?
Attempt 3...
Reading around, eg here http://www.klundberg.com/blog/capturing-objects-weakly-in-instance-method-references-in-swift/ it seems that my approach above will lead to strong reference cycles, and that what I need to do is pass the static method eg Movement.move rather than moveComponent.move. So the type signature I would be storing would actually be (Component) -> (Any?) -> Void rather than (Any?) -> Void. But my question still stands, I still would like to be able to store an array of these static methods with a bit more type-safety than just [Any].
One approach to casting the parameters of a closure, suggested in Mike Ash's blog that Casey Fleser linked to, is to "recurry"(?) it.
A genericised Binding class:
private class Binding<Argument>{
weak var listener: AnyObject?
let action: AnyObject -> Argument -> ()
init(listener: AnyObject, action: AnyObject -> Argument -> ()) {
self.listener = listener
self.action = action
}
func invoke(data: Argument) -> () {
if let this = listener {
action(this)(data)
}
}
}
And the event manager, without the recurrying:
class EventManager {
var events = [EventName: [AnyObject]]()
func add<T: AnyObject, Argument>(name: EventName, listener: T, action: T -> Argument -> Void) {
let binding = Binding(listener: listener, action: action) //error: cannot convert value of type 'T -> Argument -> Void' to expected argument type 'AnyObject -> _ -> ()'
if var eventArray = events[name] {
eventArray.append(binding)
} else {
events[name] = [binding]
}
}
func dispatch<Argument>(name: EventName, argument: Argument) {
if let eventArray = events[name] {
for element in eventArray {
(element as! Binding<Argument>).invoke(argument)
}
}
}
func remove(name: EventName, listener: Component) {
if var eventArray = events[name] {
eventArray = eventArray.filter(){ $0 !== listener }
}
}
}
This still produces the same error, of not being able to cast to AnyObject:
error: cannot convert value of type 'T -> Argument -> Void' to expected argument type 'AnyObject -> _ -> ()'.
Bu if we call the first part of the curried function, and enclose it within a new closure (I don't know if this has a name, I'm calling it "recurrying"), like this: action: { action($0 as! T) } then it all works (technique taken from Mike Ash). I guess this is a bit of a hack, in that Swift type safety is being circumvented.
I also don't really understand the error message: it's saying it can't convert T to AnyObject, but then accepts casting to T?
EDIT: updated with the complete code so far
edit2: corrected how events are appended
edit3: removing events now works
//: Playground - noun: a place where people can play
import Cocoa
import Foundation
enum EventName: String {
case input, update
}
private class Binding<Argument>{
weak var listener: AnyObject?
let action: AnyObject -> Argument -> ()
init(listener: AnyObject, action: AnyObject -> Argument -> ()) {
self.listener = listener
self.action = action
}
func invoke(data: Argument) -> () {
if let this = listener {
action(this)(data)
}
}
}
class EventManager {
var events = [EventName: [AnyObject]]()
func add<T: AnyObject, Argument>(name: EventName, listener: T, action: T -> Argument -> Void) {
let binding = Binding(listener: listener, action: { action($0 as! T) }) //
if events[name]?.append(binding) == nil {
events[name] = [binding]
}
}
func dispatch<Argument>(name: EventName, argument: Argument) {
if let eventArray = events[name] {
for element in eventArray {
(element as! Binding<Argument>).invoke(argument)
}
}
}
func remove<T: AnyObject, Argument>(name: EventName, listener: T, action: T -> Argument -> Void) {
events[name]? = events[name]!.filter(){ ( $0 as! Binding<Argument>).listener !== listener }
}
}
// Usage test
//Components
class Component {
weak var events: EventManager?
let doc: String
init(doc: String){
self.doc = doc
}
}
class Input: Component {
init() {
super.init(doc: "InputComponent")
}
func goRight() {
events?.dispatch(.input, argument: CGVector(dx: 10, dy: 0) )
}
func goUp() {
events?.dispatch(.input, argument: CGVector(dx: 0, dy: -5) )
}
}
class Movement: Component {
init() {
super.init(doc: "MovementComponent")
}
func move(vector: CGVector) {
print("moved \(vector)")
}
}
class Physics: Component {
init() {
super.init(doc: "PhysicsComponent")
}
func update(time: NSTimeInterval){
print("updated at \(time)")
}
func move(vector: CGVector) {
print("updated \(vector)")
}
}
// Entity
class Entity {
let events = EventManager()
}
class someClass: Entity {
// components
let inputComponent: Input
let moveComponent: Movement
let physicsComponent: Physics
override init() {
inputComponent = Input()
moveComponent = Movement()
physicsComponent = Physics()
super.init()
inputComponent.events = events
events.add(.input, listener: moveComponent, action: Movement.move)
events.add(.input, listener: physicsComponent, action: Physics.move)
}
}
let someInstance = someClass()
someInstance.inputComponent.goRight()
//moved CGVector(dx: 10.0, dy: 0.0)
//updated CGVector(dx: 10.0, dy: 0.0)
someInstance.events.remove(.input, listener: someInstance.moveComponent, action: Movement.move)
someInstance.inputComponent.goUp()
//updated CGVector(dx: 0.0, dy: -5.0)
someInstance.events.remove(.input, listener: someInstance.physicsComponent, action: Physics.move)
someInstance.inputComponent.goRight()
// nothing
A different approach to how to store a collection of different type signatures. Instead of using generics, casting, or type erasure, use an enum with associated types representing each type of signature you want to use eg
enum Signature{
case cgvector(CGVector -> Void)
case nstimeinterval(NSTimeInterval -> Void)
The disadvantage is that the enum captures a strong reference to the method. However (I need to take this out of a playground to test it more), this doesn't seem to create a strong reference cycle. You can set the containing entity to nil and all of its components appear to be deinitialized. I'm not quite sure what's happening there. To me this enum approach seems cleaner than putting a generic wrapper in an array of AnyObject and have to cast and type-erase constantly.
Comments and criticisms welcome.
/*:
## Entity - Component framework with a notification system for decoupled communications between components
### Limitations:
1. Closure class stores a strong reference to the components. But, a strong reference cycle is not created.
2. A given class instance (component) can only subscribe to a given event with one method.
*/
import Cocoa
import Foundation
enum EventName: String {
case onInput
}
//A type-safe wrapper that stores closures of varying signatures, and allows them to be identified by the hashValue of its owner.
class Closure {
enum Signature {
case cgvector(CGVector -> Void)
case nstimeinterval(NSTimeInterval -> Void)
func invoke(argument: Any){
switch self {
case let .cgvector(closure): closure(argument as! CGVector)
case let .nstimeinterval(closure): closure(argument as! NSTimeInterval)
}
}
}
var method: Signature
weak var owner: Component?
init(owner: Component, action: Closure.Signature) {
method = action
self.owner = owner
}
}
// Entity
class Entity {
var components = Set<Component>()
private var events = [EventName: [Closure]]()
deinit {
print("Entity deinit")
}
// MARK: component methods
func add(component: Component){
components.insert(component)
component.parent = self
}
func remove(component: Component){
unsubscribeFromAll(component)
components.remove(component)
}
func remove<T: Component>(type: T.Type){
guard let component = retrieve(type) else {return}
remove(component)
}
func retrieve<T: Component>(type: T.Type) -> T? {
for item in components {
if item is T { return item as? T}
}
return nil
}
// MARK: event methods
func subscribe(listener: Component, method: Closure.Signature, to event: EventName ){
let closure = Closure(owner: listener, action: method)
// if event array does not yet exist, create it with the closure.
if events[event] == nil {
events[event] = [closure]
return
}
// check to make sure this listener has not subscribed to this event already
if ((events[event]!.contains({ $0.owner! == listener })) == false) {
events[event]!.append(closure)
}
}
func dispatch(argument: Any, to event: EventName ) {
events[event]?.forEach(){ $0.method.invoke(argument) }
}
func unsubscribe(listener: Component, from name: EventName){
//events[name]? = events[name]!.filter(){ $0.hashValue != listener.hashValue }
if let index = events[name]?.indexOf({ $0.owner! == listener }) {
events[name]!.removeAtIndex(index)
}
}
func unsubscribeFromAll(listener: Component){
for (event, _) in events {
unsubscribe(listener, from: event)
}
}
}
//Components
class Component: Hashable {
weak var parent: Entity?
var doc: String { return "Component" }
var hashValue: Int { return unsafeAddressOf(self).hashValue }
deinit {
print("deinit \(doc)")
}
}
func == <T: Component>(lhs: T, rhs: T) -> Bool {
return lhs.hashValue == rhs.hashValue
}
//: #### Usage test
class Input: Component {
override var doc: String { return "Input" }
func goRight() {
parent?.dispatch(CGVector(dx: 10, dy: 0), to: .onInput )
}
func goUp() {
parent?.dispatch(CGVector(dx: 0, dy: -10), to: .onInput )
}
}
class Movement: Component {
override var doc: String { return "Movement" }
func move(vector: CGVector) {
print("moved \(vector)")
}
}
class Physics: Component {
override var doc: String { return "Physics" }
func update(time: NSTimeInterval){
print("updated at \(time)")
}
func move(vector: CGVector) {
print("updated \(vector)")
}
}
// an example factory
var entity: Entity? = Entity()
if let instance = entity {
// a couple of ways of adding components
var inputComponent = Input()
instance.add(inputComponent)
instance.add(Movement())
instance.add(Physics())
var m = instance.retrieve(Movement.self)
instance.subscribe(m!, method: .cgvector(m!.move), to: .onInput)
let p = instance.retrieve(Physics.self)!
instance.subscribe(p, method: .cgvector(p.move), to: .onInput)
inputComponent.goRight()
inputComponent.goUp()
instance.retrieve(Input.self)?.goRight()
instance.remove(Movement.self)
m = nil
inputComponent.goRight()
}
entity = nil //not a strong ref cycle
I fell into that situation but i found a cool solution
with an anonymous inline function, it is like mapping
here is an example
var cellConfigurator: ((UITableViewCell, _ index: IndexPath) -> Void)?
func setup<CellType: UITableViewCell>(cellConfig: ((CellType, _ index: IndexPath) -> ())?)
{
// this mini function maps the closure
cellConfigurator = { (cell: UITableViewCell, _ index: IndexPath) in
if let cellConfig = cellConfig, let cell = cell as? CellType {
cellConfig(cell, index)
}
else
{ print("-- error: couldn't cast cell") }
}
}

How to observe an array of NSObjects in swift?

I am new in swift language and my problem is about how to use observable/observer pattern in swift.
I want to make my array to be observable in my SocketManager class so it can be observed by my UIViewController class. I have used the Observable class written by Andrew J Wagner which I got from this link:
http://www.drewag.me/posts/swift-kvo-substitute-observable-variables
I have the array:
var marketIndexList: Array< MarketIndex > = []
which will get its data from a server. This list will be updated every time a new data received from server. After I got the values of my Array from server I want to make it of type Observable class which is implemented by the above link:
marketIndexList = Observable(marketIndexList)
But I got this error:
'MarketIndex' is not identical to 'AnyObject'
MarketIndex is a class of type NSObject which has some properties of type String.
This is the Observable class that I have used:
import Foundation
class Observable {
typealias DidChangeHandler = (oldValue: Array<MarketIndex>?, newValue: Array<MarketIndex>) -> ()
var value : Array<MarketIndex> = [] {
didSet {
for (owner, handlers) in self.observers {
for handler in handlers {
handler(oldValue: oldValue, newValue: value)
}
}
}
}
init(_ value: Array<MarketIndex>) {
self.value = value
}
func addObserverForOwner(owner: IndexViewController, triggerImmediately: Bool, handler: DidChangeHandler) {
if let index = self.indexOfOwner(owner) {
// since the owner exists, add the handler to the existing array
self.observers[index].handlers.append(handler)
} else {
// since the owner does not already exist, add a new tuple with the
// owner and an array with the handler
self.observers.append(owner: owner, handlers: [handler])
}
if (triggerImmediately) {
// Trigger the handler immediately since it was requested
handler(oldValue: nil, newValue: self.value)
}
}
func removeObserversForOwner(owner: AnyObject) {
if let index = self.indexOfOwner(owner) {
self.observers.removeAtIndex(index)
}
}
// #pragma mark - Private Properties
var observers: [(owner: IndexViewController, handlers: [DidChangeHandler])] = []
// #pragma mark - Private Methods
func indexOfOwner(owner: AnyObject) -> Int? {
var index : Int = 0
for (possibleOwner, handlers) in self.observers {
if possibleOwner === owner {
return index
}
index++
}
return nil
}
}
Can anyone tell me what the problem is?
Also does anyone know a way to observe an array of objects in swift?
I would appreciate any help.
Thanks in advance.
The error is because marketIndexList is defined as Array<MarketIndex> but you assigned Observable instance. Perhaps you wanted to do something like this:
var observableList: Observable = Observable([])
var marketIndexList: Array<MarketIndex> = [MarketIndex(), MarketIndex()]
observableList.value = marketIndexList
// Or maybe
observableList = Observable(marketIndexList)
By the way, you can also use Objective-C KVO from Swift. Just mark the property as dynamic and make sure the class inherits NSObject to make the property observable. For example:
class ObservableClass: NSObject {
dynamic var value = [Int]()
}
This post is good to read for KVO in Swift in addition to what you referred to.
https://medium.com/proto-venture-technology/the-state-of-kvo-in-swift-aa5cb1e05cba

Resources