[swift-users] Memory management without ARC?

Matthias Zenger matthias at objecthub.net
Sat Jan 30 12:56:05 CST 2016


> So, my question is: What are Swift programmers supposed to do if they are dealing with inherently cyclic data structures that cannot be broken up via weak references? It almost seems like there's some memory management functionality missing in Swift that makes this possible.

You need to write code that walks through the data structures, recognizes which parts are no longer live, and clears the strong references to them. This is one of the age-old fundamental problems with reference counting — I remember dealing with it in Smalltalk-80, whose GC was based on refcounts.

I think you’re going to have to end up writing a mark/sweep garbage collector for your Scheme interpreter. A basic implementation is really simple.

That's what I ended up doing. It just feels like a huge hack: it messes up your code and the impact on the performance of the interpreter is serious.

Just off the top of my head, I think you’d use only unowned references between your Scheme objects. Then to keep the objects alive, you have a master array/set/whatever that holds strong references to all of them. After doing the recursive marking of all your objects, you traverse the master set and remove all the objects whose mark flags aren’t set.

Yes, that was my first attempt. The problem is that Swift 2 has no good support for weak references beyond weak properties. I'm using arrays throughout my code and those use strong references. Furthermore, my basic data structure is an enum with associated values. I haven't figured out a way to refer to the associated values weakly. This doesn't work:

  enum SchemeValue {
    ...
    case Vector(weak WrappedArray<SchemeValue>)
  }

So, what I ended up doing is to implement a mark/sweep garbage collector which is holding only weak references in an object pool. All references in the application are strong. After the mark phase, I then clear objects that are not marked but that are still weakly linked in the object pool. These are the objects that have cyclic dependencies. I just clear them (reset all references) and ARC deallocates them.

This approach has the same implementation problem I describe above when it comes to implement an array of weak references. I use something like this:

  struct WeakReference<T: AnyObject> {
    weak var obj: T?
  }
  class ObjectPool<T: AnyObject> {
    var references: [WeakReference<T>]
    ...
  }

It does have the benefit that this ugly workaround via struct WeakReference<T> is only needed in the ObjectPool<T> implementation and isn't spread throughout the whole codebase.

Nevertheless, this collaborative garbage collection scheme where the mark/sweep collector handles cycles and ARC handles the rest doesn't feel like a good solution. It's, at best, a workaround that is impossible to optimize. This is why I was wondering if there's anything else I could do...

== Matthias

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20160130/2b62c60f/attachment.html>


More information about the swift-users mailing list