Underscore attributes: @_implicitSelfCapture


Greetings, traveler!

Did you notice that using Task does not require explicit use of self? That’s because the Task’s closure is marked with the @_implicitSelfCapture attribute. The key idea is that the Task will be completed at some point, and all captured objects will be released.

Note

There is a warning in Swift documentation about underscore attributes:

Usage of these attributes outside of the Swift monorepo is STRONGLY DISCOURAGED.

The semantics of these attributes are subject to change and most likely need to go through the Swift evolution process before being stabilized.

All the stable attributes are described here.

Example

We can use the same approach in our code. Consider this example.

class Client {
    
    func configure() {
        
        Task {
            action()
        }
        
        perform {
            action() // ❌ Error
        }
    }
    
    func action() {}
    
    func perform(@_implicitSelfCapture completion: @escaping () -> Void) {}
    
}

While the Task’s completion will not produce any errors, the performing method will.

❌ Call to method ‘action’ in closure requires explicit use of ‘self’ to make capture semantics explicit.

Reference ‘self.’ explicitly.

Capture ‘self’ explicitly to enable implicit ‘self’ in this closure.

To fix this issue, we can explicitly use self like this:

perform { [self]
    action()
}

Or like this:

perform {
    self.action()
}

But there is another way to avoid errors. Just mark the completion parameter with the @_implicitSelfCapture attribute, and you will gain the same result.

func perform(@_implicitSelfCapture completion: @escaping () -> Void) {}

Now, let’s check out the entire code.

class Client {
    
    func configure() {
        
        Task {
            action()
        }
        
        perform {
            action()
        }
    }
    
    func action() {}
    
    func perform(@_implicitSelfCapture completion: @escaping () -> Void) {}
    
}

Conclusion

This attribute can be used when dealing with entities that rely on reference capturing but will eventually be released from memory. But we should remember the warning from the beginning of this article.