[NSString UTF8String] and ARC

Without ARC, this kind of code is feasible. (This example is extremely simplified just for demonstration purposes. Please don't get hung on the details.)


const char* someStr(int i)
{
    return [NSString stringWithFormat: @"%i", i].UTF8String;
}


While it is dangerous in the sense that the char* will become invalidated the next time autoreleased objects are released, as long as it's documented in this function's usage (ie. as long as the returned string is not used after the next autorelease cycle), there should be no problem.


However, what happens if ARC is being used? Will ARC destroy the NSString object immediately when it sees it going out of scope, thus immediately invalidating the char*, as the function returns?

First, from the documentation of -UTF8String:

This C string is a pointer to a structure inside the string object, which may have a lifetime shorter than the string object and will certainly not have a longer lifetime. Therefore, you should copy the C string if it needs to be stored outside of the memory context in which you use this property.


Second, note that the declaration of -UTF8String is annotated with NS_RETURNS_INNER_POINTER. This is an alis for objc_returns_inner_pointer, which is documented here. From that:

When such a message is sent to an object, the object’s lifetime will be extended until at least the earliest of:

  • the last use of the returned pointer, or any pointer derived from it, in the calling function or
  • the autorelease pool is restored to a previous state.


So, if you were to assign the result from -UTF8String to a local variable, you could rely on it continuing to be valid long enough to, say, strdup() it. But returning it from your function is not safe, since it then survives past "the last use of the returned pointer, or any pointer derived from it, in the calling function".


If you want to implement a function with semantics like this, you may need to use Core Foundation. Do a CFBridgingRetain() on the NSString object and then CFAutorelease() it. This makes sure that the string object survives until the autorelease pool is drained. Alternatively, you can create a CFData object from the C string and CFAutorelease() that. You'd return its byte pointer.

According to the documentation:


This C string is a pointer to a structure inside the string object, which may have a lifetime shorter than the string object and will certainly not have a longer lifetime. Therefore, you should copy the C string if it needs to be stored outside of the memory context in which you use this property.


That is, the pointer is no longer valid after the string goes out of scope, which is a lot more stringent than no longer referenced.


a. ARC won't do anything with the pointer value, because it's just an arbitrary pointer, not an object.


b. There's no API contract that says the returned value is a pointer to a retainable, or even malloc'ed, memory block at all. It may be an interior pointer within another data structure. This may vary with the underlying string, since different strings may have different internal storage mechanisms.


Your code was never feasible. You were just (un)lucky that you never saw it crash.

[NSString UTF8String] and ARC
 
 
Q