Martin Perry
Martin Perry

Reputation: 9527

Swift C callback - takeUnretainedValue or takeRetainedValue for Swift class pointer

I have several UIView or UITableViewCell. Inside I have C callback, for example:

CCallback(bridge(self),
       {(observer, data) -> Void in
          let mySelf = Unmanaged<DetailedView>.fromOpaque(observer!).takeRetainedValue()
           mySelf.fillLoadedData(data: data)
        });

Somewhere else

func bridge<T : AnyObject>(_ obj : T) -> UnsafeMutableRawPointer {
    return UnsafeMutableRawPointer(Unmanaged.passUnretained(obj).toOpaque())
}

In C:

void CCalback(void * classPtr, void(*callback)(void *, MyData)){
   //fill data
   callback(classPtr, data)
}

Should I use takeUnretainedValue or takeRetainedValue in closure? As far as I understand this, retained will increase objects reference count, so it wont be auto-destructed? Otherwise, if I use takeUnretainedValue, if self is auto-released, this will crash, so using takeRetainedValue will prevent it. Am I correct?

Upvotes: 3

Views: 877

Answers (1)

Martin R
Martin R

Reputation: 539815

An object pointer can be converted to a Unsafe(Mutable)RawPointer (the Swift equivalent of the C void *) with or without retaining the object:

let ptr = UnsafeMutableRawPointer(Unmanaged.passUnretained(obj).toOpaque())
// Does not retain `obj`

let ptr = UnsafeMutableRawPointer(Unmanaged.passRetained(obj).toOpaque())
// Retains `obj`

The conversion back to an object pointer (often done in a callback function called from C) can be with or without consuming a retain:

let obj = Unmanaged<T>.fromOpaque(ptr).takeUnretainedValue()
// Does not consume a retain

let obj = Unmanaged<T>.fromOpaque(ptr).takeRetainedValue()
// Consumes a retain

If the lifetime of obj is guaranteed while the callback is active then the easiest way is to use the "unretained" conversions in both directions. You are responsible for retaining obj while the callback is active, e.g. by unregistering the callback before obj is deinitialized.

The alternative is to use passRetained() to convert obj to a pointer. This retains the object and therefore keeps it "alive". The callback can still use the "unretained" conversion to convert the pointer to an object pointer, without decreasing the retain count. But there must be exactly one takeRetainedValue() call to consume the retain. After that, the object can be destroyed if there are no other references to it.

More generally, each call to passRetained(obj) increases the retain count and each call to takeRetainedValue() decreases it, so they must be properly balanced.

Upvotes: 4

Related Questions