Renaming of weakified arguments

memory-safety
weakify
capture-list
strongify-weak

(Alexander Cyon) #1

When we use the weak modifier before either self or some property (reference type, not struct) of self in a capture list we create a new local variable having the same name as the captured type but being weak. This is a bit odd. The resulting code is really confusing if you want to use good variables names. Have a look at the code in RxSwift extenison doing the weakify, strongify dance:

func weakify<A: AnyObject, B>(_ obj: A, method: ((A) -> (B) -> Void)?) -> ((B) -> Void) {
    return { [weak obj] value in
        guard let obj = obj else { return }
        method?(obj)(value)
     }
}

The naming of the object is just obj, which is not so swifty. When we attempt to replace obj more descriptive names we see that it easily gets MORE confusing:

 func weakify<A: AnyObject, B>(_ strongObject: A, method: ((A) -> (B) -> Void)?) -> ((B) -> Void) {
        return { [weak strongObject] value in // strongObject is actually now weak, thanks to the `weak` in the capture list
            let weakObject: A? = strongObject // correcting the confusing name `strongObject`, which actually is weak
            guard let strongAgain = weakObject else { return } // we have now strongified the weakified initial object
            method?(strongAgain)(value)
        }
    }

If we could weakify and also rename the captured type as an action we could get more easily read code:

func weakify<A: AnyObject, B>(_ strongObject: A, method: ((A) -> (B) -> Void)?) -> ((B) -> Void) {
    return { [weak strongObject -> weakObject] value in
        guard let object = weakObject else { return }
        method?(object)(value)
    }
}

What do you say?


(Jon Shier) #2

Better solution would just be to automate the unwrapping inside the closure or weak capture in general. But until that's available, I think shadowing is the Swifty solution. If you don't like obj as a name, give it a better name (which weak/strongObject certainly is not).

func weakify<A: AnyObject, B>(_ reference: A, method: ((A) -> (B) -> Void)?) -> ((B) -> Void) {
    return { [weak reference] value in
        guard let reference = reference else { return }
        method?(reference)(value)
     }
}

#3
func weakify<A: AnyObject, B>(_ strongObject: A, method: ((A) -> (B) -> Void)?) -> ((B) -> Void) {
    return { [weak weakObject = strongObject] value in
        guard let object = weakObject else { return }
        method?(object)(value)
    }
}

(Joe Groff) #4

You can already do this:

class C {}

var x = C()

let closure = {[weak y = x] in print(y) }

closure() // should print Optional(foo.C)
x = C()

closure() // should print nil

(Alexander Cyon) #5

Oh I have missed that completely, nice @linqingmo @Joe_Groff ! I found it in the swift docs now:
https://docs.swift.org/swift-book/ReferenceManual/Expressions.html

// Weak capture of "self.parent" as "parent"
myFunction { [weak parent = self.parent] in print(parent!.title) }