Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 205
Martin Perry Avatar asked Nov 24 '25 07:11

Martin Perry


1 Answers

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.

like image 182
Martin R Avatar answered Nov 26 '25 22:11

Martin R



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!