I would like to bring up a little update.
As always, I would appreciate if anyone points me in the right direction.
The reason why I post this messages and why I do not consider this being spam.
I am aware, that this thread could be perceived as using The Swift Forums as my personal notebook.
I would like to assure reader, that I in fact have a personal notebook
I created this thread (and make this posts) since I think, that closures are one of the most important concepts in Swift. Issues related to closures (and closures related debugging) is more and more common.
At the same time, I find it difficult to debug closures related incidents and use tools like "Xcode Memory Graph" (especially compared to classes). At the same time, compared to classes, there is not a lot of content (either in documentation or blog posts) about how to debug closures and how the closured work. Most of the content reserves to a simple abstraction, that closure context is living on heap and is reference counted.
Therefore I have to dig deeper to understand this problem. And since my knowledge of inner workings of the Swift compiler (and LLVM) and various IL and optimalizations is insufficient, I share my current knowledge and thoughts.
Since the last post
I have created little swift file. In that file, I have created few closures with one or two strong class captures and zero or one mutable value capture.
I have observed, that closures with less than two strong class captures do call retain on captured instance before each call. Unlike closure with more strong class captures which calls retain/release when context is initialized/destroyed.
At this point I assumed, that there are differences (possibly optimiziations that apply) based on the number and nature of closure capture list arguments.
I have tried to search for more information in the SIL. I have used --sil-print-all
on the file. I have observed that all closures were initialized using SIL "command" %r = partial_apply ...
and the %r
was then subject to strong_retain %r
and strong_release %r
on each assignment. I assume that the %r
represents the function pointer and instance of a closure context.
If this is the case, I assume, that there should be some piece of compiler code (possibly amongst multiple SIL optimization passes) that is responsible for the difference of Closure Context behaviour based on the context content and I should be able to observe there, how the context is allocated and reference counted (if there is a context at all). I would really appreciate to know where to find more about this.
Further, I wonder, whether I would be able to intercept context allocation by hooking a custom piece of code to swift_allocObject
in the same manner as with swift_retain
. (Note: Since posting this, I was able to observe in assembly, that such calls are present around the breakpoint when assigning certain closures.) And If I am correct, that the context is not allocated in a certain scenarios (for example, when context captures strongly only 1 class instance), that my initial observation How do closures work (memory management) is incorrect and contexts are in fact HeapObejcts and I have based on my observations on a wrong piece of code.
Enjoy your weekend!
-stuchlej