How to transfer ownership out of an #autoreleasepool with ARC - objective-c

I have the following code
- (NSString *)stringByEscapingXMLEntities;
{
NSString *result;
#autoreleasepool {
result = [self stringByReplacingOccurrencesOfString:#"&" withString:#"&"];
result = [result stringByReplacingOccurrencesOfString:#"\"" withString:#"""];
// ... lot of -stringByReplacingOccurrencesOfString: calls
result = [result stringByReplacingOccurrencesOfString:#" " withString:#" "];
}
return result;
}
I ask myself now how would I transfer ownership result out of the method. Before ARC I would have retained result before exiting the autorelease block and returned it autoreleased at the end of the method.
Thanks!

There are two ways to do that:
Rename the method to something like copyStringByEscapingXMLEntities -- the copy indicates the transfer of ownership and ARC creates the code accordingly.
Append, in the header, NS_RETURNS_RETAINED to the method definition like this: - (NSString *)stringByEscapingXMLEntities NS_RETURNS_RETAINED.
EDIT: As 'iljawascoding' mentioned, the #autoreleasepool has no real need to be kept around -- except for optimization.
EDIT 2: And remember: ARC always does the right thing. All the things you tried (your comment) result in the very same correct program -- albeit with the lack of some optimization if result was defined as __strong.

Get rid of the autorelease altogether. According to Apple, ARC will automatically insert the necessary release after your code is done with the temporary instances stored in 'result'. There's nothing to gain by rolling your own autorelease pool here.

The code you have posted is correct. Any crash has a different cause.
Because result is a strong reference outside the autorelease scope, ARC is responsible for keeping it alive when exiting the pool, and it does. You don’t need to do anything special.
More specifically, ARC generates code equivalent to this:
void *_arp = objc_autoreleasePoolPush();
temp1 = objc_retainAutoreleasedReturnValue([self stringByReplacingOccurrencesOfString:#"&" withString:#"&"]);
temp2 = objc_retainAutoreleasedReturnValue([temp1 stringByReplacingOccurrencesOfString:#"\"" withString:#"""]);
objc_release(temp1);
result = objc_retainAutoreleasedReturnValue([temp2 stringByReplacingOccurrencesOfString:#" " withString:#" "]);
objc_release(temp2);
// result is not released here
objc_autoreleasePoolPop(_arp);
return objc_autoreleaseReturnValue(result); // Result is returned autoreleased, or handed off to a matching objc_retainAutoreleasedReturnValue() in the caller.
Note that the temporary variables are handled with objc_retainAutoreleasedReturnValue/objc_release pairs. Because of the runtime optimization implemented by objc_retainAutoreleasedReturnValue and objc_retainAutoreleasedReturnValue, this means that the temporary values will actually be released immediately without ever being put in an autorelease pool if -stringByReplacingOccurrencesOfString: is built with ARC. My guess would be that most of the system frameworks aren’t yet, though.

Why not use [[NSString alloc] initWithString:result] before the #autoreleasepool scope closes? But why the ARP in the first place?

Related

Init method, memory management

I'm curious about the memory I'm allocating with my factory method. The idea is, I'll read in a file, and I'll instantiate objects by sending a single unparsed CSV line to my factory method, as such:
-(id)initWithCSV:(NSString *)csv
{
if(self = [super init])
{
NSArray *values = [csv componentsSeparatedByString:#","];
self.city = [values[0] stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceCharacterSet]];
self.country = [values[1] stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceCharacterSet]];
self.latitude = [values[2] doubleValue];
self.longitude = [values[3] doubleValue];
}
return self;
}
My first question is, how is this on memory management? I'm not completely clear on how ARC works. My values array is destroyed as soon as I return self; and exit the method, right?
Second question... is there a more efficient way to accomplish what I'm trying to do here?
In regards to your first question:
In this case, ARC doesn't actually make a difference. Since the commentsSeparatedByString: method already returns an auto-released object, and you aren't increasing it's retain count (or in ARC terms, creating any strong references), it will be cleaned up automatically when the method returns.
As for efficiency, you could probably save some memory by using NSScanner to scan up to each comma, instead of creating an array with a bunch of strings. But since this is only a few strings that are relatively short, in practice it doesn't really matter.
TL/DR: If you're using ARC, your method is very memory efficient.
This actually showcases an interesting feature of ARC, having to do with the fact that the output of componentsSeparatedByString: is autoreleased.
Say you had this function:
while ((line = [self readLine])) {
[self.lines addObject:[[MyObj alloc] initWithCSV:line];
}
Without ARC, this seemingly efficient function could consume a lot of memory because the object in values is retained until the autorelease pool is drained. (Same with line above actually.) One traditional way to optimize this is to do as follows:
while ((line = [self readLine])) {
#autoreleasepool {
[self.lines addObject:[[MyObj alloc] initWithCSV:line];
}
}
This way, when you hit the the end of the pool, the autoreleased values is deallocated. You of course, take a performance hit for draining the pool.
An naive ARC implementation would not solve this problem. It would see the +0 output of componentsSeparatedByString:, call [values retain] and call [values release] at the end of scope.
What ARC actually does is call a function objc_retainAutoreleasedReturnValue(values) which instead of retaining values, removes it from the autorelease pool. That way when it calls [values release] at the end of scope, the array is deallocated. Likewise, it smartly releases line at each iteration of my loop.
This provides excellent memory and CPU efficiency.
I think that NSArray is released since the scope is limited in the init due to autorelease pools. But the values are copied in self.latitude for example.

Using alloc, init in ARC enabled projects

Actually I am working on a project with ARC enabled. I know using alloc and init is taking ownership of the object. I know, If I create a string like this
NSString *myString = [[NSString alloc]initWithFormat:#"Something"];
then I need to release the myString on myself. What If I am using ARC enabled? I cannot release on myself. So will it create a leak? Or should I don't create object like this?
I can create a string like below code too.
NSString *myString = [NSString stringWithFormat:#"Something"];
But which type I need to use exactly for ARC enabled project ? What will happen if I use first type?
If you use ARC, all the necessary release calls will be added for you when you compile. It will not leak.
The difference between
NSString *myString = [[NSString alloc]initWithFormat:#"Something"];
and
NSString *myString = [NSString stringWithFormat:#"Something"];
is that the first one will automatically released after the last reference to myString in that block, while the second one is an autoreleased instance that will only be released at the end of the run loop. It's not a big difference, but if you're using a lot of objects, try to avoid autoreleased ones to keep memory usage low.
ARC takes care of the memory management, so no you don't need to worry about calling release on your myString variable, ARC will do that for you. Also as a suggestion I would recommend using convenience methods to create your object such as
[NSString stringWithFormat:#"Something"];
It's enough to set the string pointer to nil to release it.
You can also do the same things that you would be able to do without ARC, but with the advantage that if you don't explicitly do anything, the ARC will manage (almost) everything for you.
So to release it you set it to nil, let's see what else you could do:
NSString* str= [[NSString alloc]initWithUTF8String: "Hello"];
// here the retain count of str is 1
__unsafe_unretained NSString* string= str;
// again 1 because string is __unsafe_unretained
void* data= (__bridge_retained void*) string;
// data retains the string, so the retain count is to 2
// This is useful in the case that you have to pass an objective-c object
// through a void pointer.You could also say NSString* data= string;
str=nil;
// Here the retain count of str is 1
NSLog(#"%#",(__bridge NSString*)data);
UPDATE
Here's why sometimes you don't notice that an object is released:
NSString* str= [[NSString alloc]initWithString: #"hey"];
__unsafe_unretained NSString* str2=str;
str=nil;
NSLog(#"%#",str2);
In this case str=[[NSString alloc]initWithString: #"hey"] is equal to str=#"hey", with the difference that str is autoreleased and not released.But the compiler optimizes the code in str=#"hello", so if you are inside an autorelease block you won't have any problem, str2 will be printed correctly.
That's why I used initWithUTF8String, to avoid that compiler optimization.

"componentsSeparatedByString" Memory leak

I am facing some strange memory leak in our existing iPad application,
Here is a function which gives memory leak in instrument
-(NSString *)retriveInfo:(NSString*)fromstring:(NSString*)searchstring
{
NSArray *arrRetrive = [fromstring componentsSeparatedByString:searchstring];
if([arrRetrive count]!=0){
if ([arrRetrive count]!=1){
NSString *strDisplayOrder = [arrRetrive objectAtIndex:1];
arrRetrive = [strDisplayOrder componentsSeparatedByString:#"<"]; //MEMORY LEAK
}
}
return [arrRetrive objectAtIndex:0];
}
Here is a input parameter
Param 1 : <displayorder>1</displayorder><filename>201103153_0100.pdf</filename><title>【1面】東日本巨大地震直撃、日経平均一時675円安[br]原発関連売られる、東電はS安</title><category>トップ・注目株</category><dirpath>/var/www/mssite/webapp/data/pdf/20110315</dirpath><TimeStamp>201103141700</TimeStamp><FirstPageImg>20110315top.png</FirstPageImg></pagedata>
Param 2: <displayorder>
Basically i want to found (parse) value between start and end tag.
(I know NSXMLParser class but asper i explain this one is existing code and if i changed in code its too much time consuming)
Any suggetion?
With Regards
Pankaj Gadhiya
The code you've posted doesn't look like it's leaking memory -- all the methods you're calling are of the autorelease type (i.e. there's no new, alloc, copy, or retain in the code).
It's probably the code you have that calls retrieveInfo and does something with the result that's leaking memory (e.g. overretaining it). The leaks tool is pointing you at the componentsSeparatedByString because that's where the memory was allocated which was eventually involved in a memory leak.
Can you show us how you call retrieveInfo and what you do with the result?
Btw, what's the point of this nested if?
if([arrRetrive count]!=0){
if ([arrRetrive count]!=1)
It's wasteful, you might as well write this and get the same effect:
if ([arrRetrive count] > 1)
That leak probably means that you're over-retaining the return value. And leaks-tool just shows you the place where it was created.
You are leaking the NSArray when you re-assign a new NSArray inside the if block. The pointer to the original array is lost, which means that the runloop cannot release the memory allocated for the first result. The memory allocated for the second result is released.
You should really use a proper parser... however to address the leak, the following will work.
-(NSString *)retriveInfo:(NSString*)fromstring:(NSString*)searchstring
{
NSArray *arrRetrive = [fromstring componentsSeparatedByString:searchstring];
NSString *result = nil;
if([arrRetrive count]!=0){
if ([arrRetrive count]!=1){
NSString *strDisplayOrder = [arrRetrive objectAtIndex:1];
result = (NSString *)[[strDisplayOrder componentsSeparatedByString:#"<"] objectAtIndex:0];
}
}
return result;
}
What do you do with the object that's returned by this method? [arrRetrive objectAtIndex:0] is likely what's being leaked.
Leaks indicates that line with componentsSeparatedByString: because that's where the strings in the array are allocated. But the array isn't leaked.
Go into Leaks, look at a leaked instance, and click that little arrow button. You'll see all the retains and releases of the leaked object, which should point you to the problem.

Why doesn't this crash?

I'm attempting to narrow down a bug to a minimum reproducible case and found something odd.
Consider this code:
static NSString *staticString = nil;
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
if (staticString == nil) {
staticString = [[NSArray arrayWithObjects:#"1", #"2", #"3", nil] componentsJoinedByString:#","];
}
[pool drain];
NSLog(#"static: %#", staticString);
return 0;
}
I'm expecting this code to crash. Instead, it logs:
2011-01-18 14:41:06.311 EmptyFoundation[61419:a0f] static: static:
However, if I change the NSLog() to:
NSLog(#"static: %s", [staticString UTF8String]);
Then it does crash.
edit a bit more info:
After draining the pool:
NSLog(#"static: %#", staticString); //this logs "static: static: "
NSLog(#"static: %#", [staticString description]); //this crashes
So apparently invoking a method on the string is good enough to get it to crash. In that case, why doesn't logging the string directly cause it to crash? Shouldn't NSLog() be invoking the -description method?
Where is the second "static: " coming from? Why isn't this crashing?
Results:
Both Kevin Ballard and Graham Lee are correct. Graham's correct in realizing that NSLog() is not invoking -description (as I was erroneously assuming), and Kevin is almost definitely correct that this is a weird stack-related issue with copying a format string and a va_list around.
NSLogging and NSString does not invoke -description. Graham elegantly showed this, and if you trace through the Core Foundation sources that do the logging, you'll see that this is the case. Any backtrace originating inside NSLog shows that it invokes NSLogv => _CFLogvEx => _CFStringCreateWithFormatAndArgumentsAux => _CFStringAppendFormatAndArgumentsAux. _CFStringAppendFormatAndArgumentsAux() (line 5365) is where all of the magic is going on. You can see that it's manually going through to find all the % substitutions. It only ends up invoking the description copy function if the type of the substitution is a CFFormatObjectType, the description function is non-nil, and the substitution hasn't already been handled by another type. Since we've shown that the description is not getting copied, it's reasonable to assume that an NSString gets handled earlier (in which case it's probably going to be doing a raw byte copy), which leads us to believe...
There's a stack error going on here, as Kevin surmises. Somehow the pointer that was pointing to the autoreleased string is getting substituted to a different object, which happens to be an NSString. So, it doesn't crash. Weird. However, if we change the type of the static variable to something else, like an NSArray, then the -description method does get called, and the program does crash as expected.
How truly and utterly strange. Points go to Kevin for being the most correct about the root cause of the behavior, and kudos to Graham for correcting my fallacious thinking. I wish I could accept two answers...
My best guess for what you're seeing is that NSLog() copies the format string (probably as a mutable copy), and then parses the arguments. Since you've dealloc'd staticString, it just so happens that the copy of the format string is being placed into the same location. This is causing you to see the "static: static: " output that you described. Of course, this behavior is undefined - there's no guarantee it will always use the same memory location for this.
On the other hand, your NSLog(#"static: %s", [staticString UTF8String]) is accessing staticString before the format string copy happens, which means it's accessing garbage memory.
Your assumption that NSLog() calls -description on an NSString instance is faulty. I just added this category:
#implementation NSString (GLDescription)
- (NSString *)description {
NSLog(#"-description called on %#", self);
return self;
}
#end
It doesn't cause a stack overflow, because it doesn't get called recursively. Not only that, but if I insert that category into the code in your question, I find this output:
2011-01-18 23:04:11.653 LogString[3769:a0f] -description called on 1
2011-01-18 23:04:11.656 LogString[3769:a0f] -description called on 2
2011-01-18 23:04:11.657 LogString[3769:a0f] -description called on 3
2011-01-18 23:04:11.658 LogString[3769:a0f] static: static:
so we conclude that NSLog() doesn't call -description on an NSString it comes across in its args. Why you get the static string twice is likely a quirk of the data on the stack when you erroneously access the released staticString variable.
Accessing dealocated memory does not necessarily cause a crash. The behavior is undefined. You are expecting too much!
Maybe it has something to do with the #"static:" being stored in the same memory location as staticString. staticString will be deallocated and it stores the #"static: %#" in that recycled mem location, so then the staticString pointer is on "static: %#" so it ends up static: static:.
This is a case of "Use after free()". What happens is "undefined behavior". Your example is really no different than:
char *stringPtr = NULL;
stringPtr = malloc(1024); // Example code, assumes this returns non-NULL.
strcpy(stringPtr, "Zippers!");
free(stringPtr);
printf("Pants: %s\n", stringPtr);
What happens at the printf line? Who knows. Anything from Pants: Zippers! to Pants: (...garbage...) Core Dump.
All the Objective-C specific stuff is actually irrelevant- it's the fact that you're using a pointer to memory which is no longer valid is the only thing that matters. You're better off throwing darts at the wall than trying to explain "why" it's not crashing and printing static: static. For performance reasons, most malloc implementations don't bother "reaping" free()'d allocations until they have to. IMHO, this is probably why your example isn't crashing in the way you were expecting it to.
If you really want to see this particular program crash, you can do one of the following:
Set the environment variable CFZombieLevel to 17 (scribble + don't free).
Set the environment variable NSZombieEnabled to YES.
Set the environment variable DYLD_INSERT_LIBRARIES to /usr/lib/libgmalloc.dylib (see man libgmalloc).

+[NSString stringWithString:] -- what's the point?

As NSString strings are immutable, what is the value of the stringWithString: class method?
I get the utility when used with NSMutableString, I just didn't see the utility with the NSString class.
You might have a NSMutableString (or some home-grown NSString subclass) that you want to duplicate.
NSMutableString *buffer = [NSMutableString string];
// do something with buffer
NSString *immutableStringToKeepAround = [NSString stringWithString:buffer];
Of course, you can also just make a copy:
NSMutableString *buffer = [NSMutableString string];
// do something with buffer
NSString *immutableStringToKeepAround = [[buffer copy] autorelease];
but you own the copy and must release or autorelease it.
As "Andy" points out in #318666, it's related to memory management, quoting:
The difference between initWithString and stringWithString is that stringWithString returns an auto-released pointer. This means that you don't need to release it specifically, since that will be taken care of next time that the auto-release pool cleans up any auto-released pointers.
initWithString, on the other hand, returns a pointer with a retain count of 1 - you do need to call release on that pointer, or else it would result in a memory leak.
(Taken from here)
Returns a string created by copying the characters from another given string
[NSString stringWithString:#"some string"]
It is equivalent to
[[[NSString alloc] initWithString:#"some string"] autorelease]
Also, if you have a pointer to an NSString, it may actually be a subclass of NSString like NSMutableString. So, if you want to store the string and be guaranteed that it doesn't change, you should make a copy of it, hence stringWithString exists.
As another use case, if (for whatever reason) you create your own subclass of NSString or NSMutableString, stringWithString: provides a handy way to instantiate it with an instance of either NSString, NSMutableString, or MyCustomString.
I often use +stringWithString: when I need to create an NSMutableString but start it with an initial value. For example:
NSMutableString * output = [NSMutableString stringWithString:#"<ul>"];
for (NSString * item in anArray) {
[output appendFormat:#"<li>%#</li>", item];
}
[output appendString:#"</ul>"];
FYI, now that we are compiling with ARC enabled, you don't have to manually release at all, ARC will insert the release calls during compile time. So how is it still different? stringWithString is still added to the autorelease pool which gets drained sometime in the future (unless you created your own autorelease pool). initWithString will have a release call right before the function ends, so if you didn't retain it somewhere in the method, you can be sure that the string is destroyed by the end of the function call. This gives you a tighter control on the memory management as opposed to using autoreleased objects.