Using @Discardableresult for Closures in Swift

Using @discardableResult for Closures in Swift

I don't think there's a way to apply that attribute to a closure. You could capture your closure in another that discards the result:

func discardingResult<T, U>(_ f: @escaping (T) -> U) -> (T) -> Void {
return { x in _ = f(x) }
}

let g = f(5)
g(3) // warns
let h = discardingResult(g)
h(4) // doesn't warn

Ignore unused warning in Swift when use a pointer to func?

Unfortunately, discardableResult only applies to method/function declarations. What you can do is to wrap the value-returning function in a void-returning closure:

@discardableResult
func f() -> Int { return 1 }

// the type annotation here is important
let someFunc: () -> Void = { f() }

Alternatively, write a function that "erases" the return type of functions:

func discardResult<R>(_ f: @escaping () -> R) -> (() -> Void) {
{ _ = f() }
}

let someFunc = discardResult(f)

The downside is that you need to write an overload of this for each arity:

func discardResult<T, R>(_ f: @escaping (T) -> R) -> ((T) -> Void) {
{ x in _ = f(x) }
}

func discardResult<T, U, R>(_ f: @escaping (T, U) -> R) -> ((T, U) -> Void) {
{ x, y in _ = f(x, y) }
}

func discardResult<T, U, V, R>(_ f: @escaping (T, U, V) -> R) -> ((T, U, V) -> Void) {
{ x, y, z in _ = f(x, y, z) }
}

// and so on

how to squelch result not used warnings

You can discard the result using:

_ = myBSON["key"].string.ifNotNil {
print($0}
}

Or mark your method to not warn for unused results:

extension Optional {

@discardableResult func ifNotNil<T>(_ closure:(Wrapped) -> T) -> T? {
switch self {
case .some (let wrapped):
return closure(wrapped)
case .none:
return nil
}
}
}

Reference : SE-0047

Holding and releasing Closures in an array

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 effectlet refHolder = demo()

Swift 3 Result of call to (_:parameters:completionHandler:)' is unused warning and Braced block of statements is an unused closure error

You can annotate the method it is warning you about with @discardableResult.

For example:

@discardableResult
func foobar() -> Bool {
return true
}

Empty closures in Swift 4.0

Note that Void is just an empty tuple ().

I think this is due to Remove implicit tuple splat behavior from function applications (SE-0029)

Basically this change says that you can't pass tuples as parameters anymore. You used to be able to either pass parameters by inserting stuff in the () after the function name or pass a tuple representing the parameters

func foo(a: Int, b: Int) {}

let tuple = (a: 1, b: 2)
foo(tuple)

Now you can't.

If we were to transform foo to swift 4, it would look like this:

func foo(a; Int, b: Int) {}
func foo(_ x: (a: Int, b: Int)) {}

So (Void) -> Void represents a function taking a Void parameter. You used to be able to pass nothing because Void is just an empty tuple, which, after the "splat" becomes "no parameters".



Related Topics



Leave a reply



Submit