Edit (after discussion):
It's a common practice in SwiftUI to have a View (struct) hold an ObservableObject. Since a struct is not a reference type, it's easy to forget that is holds one.
The questions should be really:
Can an indirect references cause a retain cycle?
Can a retain cycle involve a single pointer
The answer to both is yes.
struct -> class -> closure{struct.class}
class ViewModel: ObservableObject
...
struct MyView: View {
#ObservedObject var viewModel: ViewModel
var body: some View {
Button("Action") {
viewModel?.handler {
// ViewModel is retained here through self:
// (MyView.viewModel)
self.someAction()
}
}
}
}
In SwiftUI, it's not a good practice to 'pass yourself' to your ViewModel
Class holding an instance var pointing to itself
(discussed here How does ARC deal with circular linked lists when the external head reference is removed?)
class A {
var next: A
}
let a = A()
a.next = a // Retain cycle of an object to itself.
(Original question)
Why is holding a reference to a closure cause a memory leak even if the closure does not retain anything?
(This started with a SwiftUI issue, but I'm not sure it's related really)
So here's a simple Manager (ViewModel) that holds a closure.
class Manager: ObservableObject {
private var handler: (() -> Void)?
deinit {
print("Manager deallocated")
}
func shouldDismiss(completion: #escaping () -> Void) {
handler = completion
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.handler?()
}
}
}
Here's a view, using it:
struct LeakingView: View {
#ObservedObject var manager: Manager
let shouldDismiss: () -> Void
var body: some View {
Button("Dismiss") { [weak manager] in
manager?.shouldDismiss {
self.shouldDismiss()
}
}
}
}
Here's a ViewController activating the flow:
class ViewController: UIViewController {
var popup: UIViewController?
#IBAction func openViewAction(_ sender: UIButton) {
let manager = Manager()
let suiView = LeakingView(manager: manager) { [weak self] in
self?.popup?.view.removeFromSuperview()
self?.popup?.dismiss(animated: true)
self?.popup = nil
}
popup = LKHostingView(rootView: suiView)
popup?.view.frame.size = CGSize(width: 300, height: 300)
view.addSubview(popup!.view)
}
}
When running this, as long as the manager is holding the closure, it will leak and the Manager will not get released.
Where is the retain cycle?
Why is holding a reference to a closure cause a memory leak even if the closure does not retain anything?
Because the closure does retain something. It retains self.
For example, when you say
manager?.shouldDismiss {
shouldDismiss()
}
The second shouldDismiss means self.shouldDismiss and retains self. Thus the LeakingView retains the Manager but the Manager, by way of the closure, is now retaining the LeakingView. Retain cycle!
This is why people say [weak self] in at the start of such closures.
I would suggest that you might not want to use [weak self] pattern here. You are defining something that should happen ½ second after the view is dismissed. So you probably want to keep that strong reference until the desired action is complete.
One approach is to make sure that Manager simply removes its strong reference after the handler is called:
class Manager: ObservableObject {
private var handler: (() -> Void)?
func shouldDismiss(completion: #escaping () -> Void) {
handler = completion
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.handler?()
self.handler = nil
}
}
}
Alternatively, if the use of this completion handler is really limited to this method, you can simplify this to:
class Manager: ObservableObject {
func shouldDismiss(completion: #escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
completion()
}
}
}
Or
func shouldDismiss(completion: #escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: completion)
}
All of these approaches will make sure that Manager does not keep any strong references beyond the necessary time.
Now, the caller can choose to use [weak self] if it wants (i.e., if it doesn’t want the captured references to even survive ½ second), but if you really want the “do such-and-such after it is dismissed”, it probably wouldn’t use weak references there either. But either way, it should be up to the caller, not the manager. The manager should just ensure that it doesn’t hang on to the closure beyond the point that it is no longer needed.
Related
I'm plaing with concurrency features of Swift. I've created helper function which returns AsyncStream with values published by NSOBject implementations. Sort of code below.
func asyncStreamFor<Root: NSObject, Value> (_ root: Root, keyPath: KeyPath<Root, Value>) -> AsyncStream<Value> {
AsyncStream { continuation in
let cancellable = root.publisher(for: keyPath, options: [.initial, .new])
.sink {
continuation.yield($0)
}
continuation.onTermination = { #Sendable _ in
cancellable.cancel()
}
}
}
I'm trying to use it (and previously used it directly using publisher) for such purposes as AVPlayer properties (rate, status) observation. Usage scheme below:
class Player {
let avPlayer = AVPlayer()
var cancellable = Set<AnyCancellable>()
init() {
avPlayer.publisher(for: \.status)
.sink {
print($0)
}.store(in: &cancellable)
}
}
Release of Player's instance works properly (no reference cycle problem).
For AsyncStream, I've tried to make it simple and used scheme as below:
class Player {
let avPlayer = AVPlayer()
init() {
Task {
for await status in asyncStreamFor(avPlayer, keyPath: \.status) {
print(status)
}
}
}
}
Here, it seems like there's a reference cycle: AsyncStream instance hold reference to cancellable (in onTermination clousure), which in turn hold reference to avPlayer. Player instance is not deinitialised when dropping last reference.
Any idea how to solve the problem without explicitly cancelling Task before dropping reference to Player?
Let's consider following code:
// Just for easier testing
protocol Printer {
var delayer: Delayer { get }
}
// Retain cycle
class Printer1: Printer {
private func action() {
print("action")
}
private(set) lazy var delayer: Delayer = {
return Delayer(action)
}()
deinit {
print("deinit")
}
}
// Works fine, but weak mess
class Printer2: Printer {
private func action() {
print("action")
}
private(set) lazy var delayer: Delayer = {
return Delayer { [weak self] in self?.action() }
}()
deinit {
print("deinit")
}
}
// Questionable hack, but works fine
class Printer3: Printer {
private func action() {
print("action")
}
private(set) lazy var delayer: Delayer = {
return Delayer(weakAction)
}()
// computed property or function is also fine here
private lazy var weakAction: () -> Void = {
return { [weak self] in
self?.action()
}
}()
deinit {
print("deinit")
}
}
// Retain cycle
class Printer4: Printer {
private func action() {
print("action")
}
private(set) lazy var delayer: Delayer = {
weak var welf: Printer4? = self
return Delayer(welf?.action ?? {})
}()
deinit {
print("deinit")
}
}
// Works fine
class Printer5: Printer {
private func action() {
print("action")
}
private(set) lazy var delayer: Delayer = {
weak var welf: Printer5? = self
return Delayer { welf?.action() }
}()
deinit {
print("deinit")
}
}
class Delayer {
private var action: () -> Void
init(_ action: #escaping () -> Void) {
self.action = action
}
func run() {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in
self?.action()
}
}
}
So we have a Printer class which contains a Delayer class that takes the action on Printer and performs it delayed.
We call it something like this:
var printer: Printer? = PrinterX()
printer?.delayer.run()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
printer = nil
}
It is clear why Printer1 creates retain cycle. Action is passed into delayer with implicit strong self which cannot be released because Delayer is owned by Printer.
Printer2 is the intended way in my opinion. Obviously doesn't create retain cycle, but it is kind of mess to write all the time. Thats why I started experimenting with other solution.
I don't understand why Printer3 doesn't create retain cycle. Because weakAction is property on self, so passing it like that into Delayer should create strong reference like in Printer1.
I also don't understand why Priner4 does create retain cycle. welf is local weak reference to self, so it should not increase the reference count when passing it into the Delayer.
Strangely enough using the welf inside closure in Printer5 doesn't create retain cycle.
Questions
Can anyone please explain to me this weird behavior on Printer3, Printer4 and Printer5
I am tempted to use the Printer3 solution. Is it safe to use? As it seems almost like a bug, can I use it without worrying about it being fixed in future versions and therefore creating retain cycle in my app?
First of all, all printers are creating and retaining their own Delayer. The delayer takes a closure and, in turn, retains that closure.
Let's try to walk through them one by one.
Printer1
As you stated yourself, it's pretty clear why it's creating a retain cycle. You are passing the self.action instance method as the closure to the Delayer, and since all closures are reference types, passing self.action will retain its surrounding scope (which is Printer1).
Printer2
Again, pretty obvious here. You're explicitly capturing a weak reference to self inside the closure you're passing to Delayer, hence not creating a retain cycle.
Printer3
Here, a retain cycle is not created, because the self.weakAction property is called immediately, and its result (a closure which holds a weak reference to self) is passed on to Delayer. This, in effect, is the exact same thing as what's happening in Printer2.
Printer4
First, you're capturing a weak reference to self, and then fetching welf?.action and passing the result into Delayer. Again, welf?.action is called immediately, and the result (a pointer to an instance method) is passed on to Delayer. The weak reference to self is only kept for the duration of the surrounding scope (the lazy var creation scope), and passing the action instance method will retain self. This is identical to Printer1.
Printer5
Here, you're first creating a weak reference to self, and then you're capturing that weak reference inside a new closure that is passed to Delayer. Since self is never directly referenced in the passed closure, it will not capture self in that scope, only the welf weak reference. This is pretty much identical to Printer2, but with a slightly different syntax.
Personally, I would opt for the Printer2 way (creating a new closure, retaining a weak reference to self and using that to call self?.action). It makes for the easiest code to follow (as opposed to retaining a variable with a closure that weakly captures self). But, depending on what you're actual use case is, it might of course make sense.
I just went memory-leak hunting in the app I am working on, and noticed that the following produces a memory leak:
class SubClass {
var didCloseHandler: (() -> Void)?
}
class MainClass {
var subClass = SubClass()
func setup {
subClass.didCloseHandler = self.didCloseSubClass
}
func didCloseSubClass() {
//
}
}
This produces a retain cycle, and for good reason - didCloseHandler captures MainClass strongly, and MainClass captures SubClass strongly.
My Question: Is there a way in Swift that allows me to assign a class method to a handler without a retain cycle?
And yes, I am aware that I can do this using subClass.didCloseHandler = { [weak self] self?.didCloseSubClass() }. I'm wondering, though, if it can be done without introducing a new closure.
make a weak reference of subClass in MainClass
If you don't have strong reference to SubClass instance somewhere else - you may try wrapper like this:
func WeakPointer<T: AnyObject>(_ object: T, _ method: #escaping (T) -> () -> Void) -> (() -> Void) {
return { [weak object] in
method(object!)()
}
}
Then use it like this:
func setup() {
subClass.didCloseHandler = WeakPointer(self, MainClass.didCloseSubClass)
}
If you don't need properties from MainClass instance in didCloseSubClass implementation - you can make this method static, which will also solve your problem.
If you have strong reference to SubClass instance somewhere else and it won't be deallocated immediately - weak var subClass will do, as was already mentioned.
EDIT:
I've come up with another idea. It may look a bit more complicated, but maybe it would help.
import Foundation
class SubClass {
#objc dynamic func didCloseHandler() {
print(#function)
}
deinit {
print(" \(self) deinit")
}
}
class MainClass {
var subClass = SubClass()
func setup() {
if let implementation = class_getMethodImplementation(MainClass.self, #selector(didCloseSubClass)),
let method = class_getInstanceMethod(SubClass.self, #selector(SubClass.didCloseHandler)) {
method_setImplementation(method, implementation)
}
}
#objc func didCloseSubClass() {
print(#function)
}
deinit {
print(" \(self) deinit")
}
}
You change closure for #objc dynamic method and set it's implementation to the one from MainClass in setup().
I want to be able to pass a reference to a method on the UIViewRespresentable (or perhaps it’s Coordinator) to a parent View. The only way I can think to do this is by creating a field on the parent View struct with a class that I then pass to the child, which acts as a delegate for this behaviour. But it seems pretty verbose.
The use case here is to be a able to call a method from a standard SwiftUI Button that will zoom the the current location in a MKMapView that’s buried in a UIViewRepresentable elsewhere in the tree. I don’t want the current location to be a Binding as I want this action to be a one off and not reflected constantly in the UI.
TL;DR is there a standard way of having a parent get a reference to a child in SwiftUI, at least for UIViewRepresentables? (I understand this is probably not desirable in most cases and largely runs against the SwiftUI pattern).
I struggled with that myself, here's what worked using Combine and PassthroughSubject:
struct OuterView: View {
private var didChange = PassthroughSubject<String, Never>()
var body: some View {
VStack {
// send the PassthroughSubject over
Wrapper(didChange: didChange)
Button(action: {
self.didChange.send("customString")
})
}
}
}
// This is representable struct that acts as the bridge between UIKit <> SwiftUI
struct Wrapper: UIViewRepresentable {
var didChange: PassthroughSubject<String, Never>
#State var cancellable: AnyCancellable? = nil
func makeUIView(context: Context) → SomeView {
let someView = SomeView()
// ... perform some initializations here
// doing it in `main` thread is required to avoid the state being modified during
// a view update
DispatchQueue.main.async {
// very important to capture it as a variable, otherwise it'll be short lived.
self.cancellable = didChange.sink { (value) in
print("Received: \(value)")
// here you can do a switch case to know which method to call
// on your UIKit class, example:
if (value == "customString") {
// call your function!
someView.customFunction()
}
}
}
return someView
}
}
// This is your usual UIKit View
class SomeView: UIView {
func customFunction() {
// ...
}
}
I'm sure there are better ways, including using Combine and a PassthroughSubject. (But I never got that to work.) That said, if you're willing to "run against the SwiftUI pattern", why not just send a Notification? (That's what I do.)
In my model:
extension Notification.Name {
static let executeUIKitFunction = Notification.Name("ExecuteUIKitFunction")
}
final class Model : ObservableObject {
#Published var executeFuntionInUIKit = false {
willSet {
NotificationCenter.default.post(name: .executeUIKitFunction, object: nil, userInfo: nil)
}
}
}
And in my UIKit representable:
NotificationCenter.default.addObserver(self, selector: #selector(myUIKitFunction), name: .executeUIKitFunction, object: nil)
Place that in your init or viewDidLoad, depending on what kind of representable.
Again, this is not "pure" SwiftUI or Combine, but someone better than me can probably give you that - and you sound willing to get something that works. And trust me, this works.
EDIT: Of note, you need to do nothing extra in your representable - this simply works between your model and your UIKit view or view controller.
I was coming here to find a better answer, then the one I came up myself with, but maybe this does actually help someone?
It's pretty verbose though nevertheless and doesn't quite feel like the most idiomatic solution, so probably not exactly what the question author was looking for. But it does avoid polluting the global namespace and allows synchronous (and repeated) execution and returning values, unlike the NotificationCenter-based solution posted before.
An alternative considered was using a #StateObject instead, but I need to support iOS 13 currently where this is not available yet.
Excursion: Why would I want that? I need to handle a touch event, but I'm competing with another gesture defined in the SwiftUI world, which would take precedence over my UITapGestureRecognizer. (I hope this helps by giving some context for the brief sample code below.)
So what I came up with, was the following:
Add an optional closure as state (on FooView),
Pass it as a binding into the view representable (BarViewRepresentable),
Fill this from makeUIView,
So that this can call a method on BazUIView.
Note: It causes an undesired / unnecessary subsequent update of BarViewRepresentable, because setting the binding changes the state of the view representable though, but this is not really a problem in my case.
struct FooView: View {
#State private var closure: ((CGPoint) -> ())?
var body: some View {
BarViewRepresentable(closure: $closure)
.dragGesture(
DragGesture(minimumDistance: 0, coordinateSpace: .local)
.onEnded { value in
self.closure?(value.location)
})
)
}
}
class BarViewRepresentable: UIViewRepresentable {
#Binding var closure: ((CGPoint) -> ())?
func makeUIView(context: UIViewRepresentableContext<BarViewRepresentable>) -> BazUIView {
let view = BazUIView(frame: .zero)
updateUIView(view: view, context: context)
return view
}
func updateUIView(view: BazUIView, context: UIViewRepresentableContext<BarViewRepresentable>) {
DispatchQueue.main.async { [weak self] in
guard let strongSelf = self else { return }
strongSelf.closure = { [weak view] point in
guard let strongView = view? else {
return
}
strongView.handleTap(at: point)
}
}
}
}
class BazUIView: UIView { /*...*/ }
This is how I accomplished it succesfully. I create the UIView as a constant property in the SwiftUI View. Then I pass that reference into the UIViewRepresentable initializer which I use inside the makeUI method. Then I can call any method (maybe in an extension to the UIView) from the SwiftUI View (for instance, when tapping a button). In code is something like:
SwiftUI View
struct MySwiftUIView: View {
let myUIView = MKMapView(...) // Whatever initializer you use
var body: some View {
VStack {
MyUIView(myUIView: myUIView)
Button(action: { myUIView.buttonTapped() }) {
Text("Call buttonTapped")
}
}
}
}
UIView
struct MyUIView: UIViewRepresentable {
let myUIView: MKMapView
func makeUIView(context: UIViewRepresentableContext<MyUIView>) -> MKMapView {
// Configure myUIView
return myUIView
}
func updateUIView(_ uiView: MKMapView, context: UIViewRepresentableContext<MyUIView>) {
}
}
extension MKMapView {
func buttonTapped() {
print("The button was tapped!")
}
}
I've got a memory leak in this case, if I pass a reference to any method, the self comes with it which increases it's reference count I guess, how can I make non class types to be weak
public class Observer {
weak private var method: ((message: AnyObject) -> ())! //error here
weak private var context: AnyObject!
}
public init(method: (AnyObject -> ())?, context: AnyObject?) {
self.method = method
self.context = context
}
in another class I guess self.callback creates a strong reference to the caller object and passes on.
var observer = Observer(method: self.callback, context: self) //pass of self.callback is a strong reference
Edit:
Working on the above, my attempt using an example that further clarifies the situation using two classes. deinit never gets called.
class Test {
private var ref: Observer?
init() {
ref = Observer(method: self.callback, context: self)
}
func callback(message: AnyObject) {
}
deinit {
println("deinit test")
}
}
public class Observer {
private var method: ((message: AnyObject) -> ())?
weak private var context: AnyObject!
public init(method: (AnyObject -> ())?, context: AnyObject?) {
self.method = method
self.context = context
}
deinit {
println("deinit observer")
}
}
From looking at your code, it seems like you are talking about a retain cycle where the Test object holds onto the Observer object through the variable ref, the Observer object holds onto the closure formed by doing self.callback, which holds onto self.
Generally in such cases, you don't want the closure property itself to be weak. Rather, you want the closure to capture a weak reference to self (the Test object is passing a "callback" to itself to another object). However, that is somewhat confusing here as we are not explicitly using closure syntax (rather, you are getting a closure by accessing a method on an instance and not calling it). The problem of capturing a weak reference to self in this case was covered in this question.
The best solution is:
ref = Observer(method: {[unowned self] in self.callback($0)}, context: self)
Try this:
public class Observer {
private var method: ((message: AnyObject) -> ())?
weak private var context: AnyObject!
public init(method: (AnyObject -> ())?, context: AnyObject?) {
self.method = method
self.context = context
}
}
I tried it and it doesn't create a strong reference cycle. But I also tried with ! instead of ?, and that didn't caused as well, and I hope somebody is out there to explain that.