Hi Everyone,
I have a query regarding capturing an NWConnection instance inside the receive closure, which gets invoked whenever some raw bytes are received. I want to know whether this will create a strong retain cycle or not.
My understanding is that NWConnection holds a reference to the closure, and if I capture the NWConnection instance inside the closure, the closure will have a reference back to the connection, which, according to my understanding, creates a strong reference cycle.
Is my understanding correct? If so, how can we break the strong reference cycle — using a capture list, or is there any other way as well?
Thanks
My understanding is that NWConnection holds a reference to the closure, and if I capture the NWConnection instance inside the closure, the closure will have a reference back to the connection, which, according to my understanding, creates a strong reference cycle.
Is my understanding correct?
Sort of.
You're right that it would be holding a strong reference; however, the key word in the documentation for receive(minimumIncompleteLength..) and receiveMessage(completion:) is "single":
"Schedules a single ... completion handler"
In both cases, what NWConnection is actually doing is:
-
Retaining the block for later use.
-
Calling the block when the data is received.
-
Releasing the block after it's been called once.
That leads to here:
If so, how can we break the strong reference cycle — using a capture list, or is there any other way as well?
Strictly speaking, I believe there's already a retain cycle here as I don't think NWConnection will destroy itself as long as there is a "pending" receive block. However, NWConnection is also what's breaking that cycle by releasing the block after it's been called. FYI, this snippet from "Building a custom peer-to-peer protocol" is what this actually looks like "in practice":
func receiveNextMessage() {
guard let connection = connection else {
return
}
connection.receiveMessage { (content, context, isComplete, error) in
// Extract your message type from the received context.
if let gameMessage = context?.protocolMetadata(definition: GameProtocol.definition) as? NWProtocolFramer.Message {
self.delegate?.receivedMessage(content: content, message: gameMessage)
}
if error == nil {
// Continue to receive more messages until you receive an error.
self.receiveNextMessage()
}
}
}
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware