When to use takeUnretainedValue() or takeRetainedValue() to retrieve Unmanaged Objects in Swift?
Asked Answered
I

2

49

According to Using Swift with Cocoa and Objective-C you can use takeUnretainedValue() and takeRetainedValue()to tell Swift how to manage the memory of an object for a function like this:

func StringByAddingTwoStrings(CFString!, CFString!) -> Unmanaged<CFString>!

When do I have to use takeUnretainedValue() or takeRetainedValue()?

When I use ARC is it then always takeUnretainedValue()?

Imaginary answered 14/3, 2015 at 12:18 Comment(0)
M
86

You use takeRetainedValue when the unmanaged object has a +1 retain count and you want ARC to take care of releasing the object when you're done. For example, if you call a Core Foundation function with Create or Copy in the name (see Create Rule in the Memory Management Programming Guide for Core Foundation) which returns an unmanaged object for which you are responsible for releasing, you generally use takeRetainedValue so that it is released for you (or, if you don't do this, you have to manually release it yourself with CFRelease or similar function). You use takeUnretainedValue when ownership of the object has not been transferred to you and you therefore do not want ARC releasing the object for you when it falls out of scope.

So, as to when you call takeUnretainedValue vs takeRetainedValue, it simply depends upon what sort of object the called function returns. As a general rule of thumb, if the object was returned from a Core Foundation function with Create or Copy in the name, use takeRetainedValue. Otherwise use takeUnretainedValue.


In terms of what happens if you call the wrong method, if you call takeUnretainedValue when you're passed a +1 object (e.g. an object returned from Core Foundation function with Create or Copy in the name), your app will leak unless you explicitly CFRelease it. You may not immediately notice the occasional leak when running the app, but it can be observed by watching your app's memory usage (e.g. if you profile your app with Instruments). But if you leave these leaks unresolved, your app may eventually receive memory warnings.

On the other hand, if you call takeRetainedValue on an object which has not been retained for you (returned by a function that did not have Create or Copy in its name), the app will likely crash when the object is released. Sometimes this won't manifest itself immediately (not until the last strong reference is resolved), but it will generally result in a catastrophic failure of the app.

So judicious selection of takeUnretainedValue vs takeRetainedValue is very important.

Meimeibers answered 14/3, 2015 at 12:43 Comment(11)
How do I know from using an API if the ownership is transferred to my or not?Imaginary
To add to Rob's answer, the documentation linked to emphasizes this: "You choose which method to use based on whether the API you are invoking returns an unretained or retained object."Debauchery
@confile The purpose of the method might make it obvious - if it's returning a new object you must retain it or it will go away. If it's returning a retained object (like one item from a collection), then you don't own it so you don't retain it. The method's documentation may clarify.Debauchery
@AaronBrager Is there any indication that I did the wrong thing if I did?Imaginary
@confile If you call takeUnretainedValue when you're passed a retained object, your app will leak. You may not notice that other than your app will leak (which you can see if you use Instruments) and you may eventually get memory warnings. If you call takeRetainedValue when you're supposed to call the unretained version, the app will likely crash (though not necessarily immediately).Meimeibers
@confile "How do I know from the API if ownership is transferred?" The convention is that if the function contains Create Or Copy in the name, ownership has been transferred. If not, ownership has not been transferred.Meimeibers
It is necessary to call unmanaged.release() when we use takeRetainedValue ?Kislev
@VictorChoy - No, because after calling takeRetainedValue you now have an object that is being managed for you.Meimeibers
Perhaps worth noting that if the return value of a Create Rule function is converted to auto-release before you attempt to get a Swift managed reference to it, this converts it to a Get Rule function. E.g. an Objective-C function that ends with CFAutorelease(cfSomethingRef); before return cfSomethingRef;Footton
I faced practical difference when performing selectors: backupText = firstResponderView?.perform(sel).takeRetainedValue() as? String ?? "" caused hard-to-track-down EXC_BAD_ACCESS exception on backupText which became garbage. Issue gone after I replaced takeRetainedValue() with takeUnretainedValue()Nonparticipating
Yep, if you call takeRetainedValue when not appropriate (i.e., where the “create rule” does not apply), it will often manifest in very hard to diagnose issues.Meimeibers
S
5

Quoting from NSHipster:

https://nshipster.com/unmanaged/

An Unmanaged instance wraps a CoreFoundation type T, preserving a reference to the underlying object as long as the Unmanaged instance itself is in scope. There are two ways to get a Swift-managed value out of an Unmanaged instance:

  • takeRetainedValue() returns a Swift-managed reference to the wrapped instance, decrementing the reference count while doing so—use with the return value of a Create Rule function.

  • takeUnretainedValue() returns a Swift-managed reference to the wrapped instance without decrementing the reference count—use with the return value of a Get Rule function.

Sallust answered 2/7, 2019 at 3:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.