Delegated – automatically weak self

  • Github address for this article

Closure, which is often used in everyday code. When using it, always remember to write code like [weak self] to prevent nasty memory leaks. It’s boring for programmers to write this kind of template code, so Delegated comes into being. This allows you to register your Closure with the weak Self automatically. Let’s take a look at its basic uses.

The basic use

It is very simple to use, and can be implemented automatically with the following two steps:

  • Use @delegated statement Closure.
  • Call Closure using the delegate() function.

Let’s take a concrete example:

Final class TextField {// First step: Use @delegated declaration Closure. Delegated var didUpdate: (String) -> ()} // Step 2: Use the delegate() function to call Closure. textField.$didUpdate.delegate(to: self) { (self, text) in // `self` is weak automatically! self.label.text = text }Copy the code

Automatic weak self is achieved in the two steps above, with no circular references, no memory leaks, and no [weak self]! 🎉

Realize the principle of

Now that you know how to use it, let’s look at how it works. The code is simple, with a single Delegated. Swift file and 410 lines of code.

Description of all classes:

  • Delegated0: Corresponds to a Closure with no return value, no parameters.
  • Delegated1: Closure of a parameter with no return value.
  • Delegated2: Closure of two parameters with no return value.
  • Delegated3: Represents a three-parameter Closure with no return value.
  • ReturningDelegated0: A parameterless Closure that should return a value.
  • ReturningDelegated1: Closure of a parameter that should return a value.
  • ReturningDelegated2: Closure of two parameters that should return a value.
  • ReturningDelegated3: Closure of three parameters that should return a value.

Although the code contains the above eight classes, you essentially only need to understand any one of them, because the rest of the classes are just arguments and return values.

Here, take Delegated1 to illustrate how it works.

@propertyWrapper public final class Delegated1<Input> { public init() { self.callback = { _ in } } private var callback:  (Input) -> Void public var wrappedValue: (Input) -> Void { return callback } public var projectedValue: Delegated1<Input> { return self } } public extension Delegated1 { func delegate<Target: AnyObject>( to target: Target, with callback: @escaping (Target, Input) -> Void ) { self.callback = { [weak target] input in guard let target = target else { return } return callback(target, input) } } func manuallyDelegate(with callback: @escaping (Input) -> Void) { self.callback = callback } func removeDelegate() { self.callback = { _ in } } }Copy the code

First, analyze the Delegated1 class. You can see that it is decorated with the @propertyWrapper keyword, which is simply used to wrap normal template code. A more detailed description of @propertyWrapper can be found here.

Next, it implements parameter support for generics by defining Input, and then declares the callback attribute to store the Closure. For wrappedValue and projectedValue are built-in arguments to the overridden @PropertyWrapper.

Next, take a look at the delegate function in Extension. The delegate function takes two arguments:

  • Target: indicates the weak object.
  • Callback: Closure that is actually used.

As you can see, inside the delegate function, is a key part of the automatic weak self. Callback is reassigned:

Self. Callback = {[weak target] input in // Let target = target else {return} return callback(target, input) }Copy the code

There are two other functions in Extension whose code is also well understood:

  • ManuallyDelegate: Manage manually, that is, do not use automatic weak self. You can also see from the code that it assigns the callback directly, without the weak self operation above.
  • RemoveDelegate: Removes the proxy.

At this point, the source code analysis is finished. As you can see, the library code is still very short and snappy.

  • Delegated Project Address

conclusion

  • The template code is wrapped with @propertyWrapper.
  • Automatic weak self is implemented by reassigning callback.