Init method, memory management - objective-c

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.

Related

How to transfer ownership out of an #autoreleasepool with ARC

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?

Should I retain a object pointer parameter in every method?

I am trying to get the hang of retain / release. I get that they are a matched set. But I don't know when I have to retain references.
-(void)sampleMethod:(RandomClass *) obj {
[obj retain];
// Do stuff to object...
[obj release];
}
Is it necessary to retain (and thus release) obj?
I am worried about obj going away. Does it follow that you must (if) retain reference parameters as soon as possible in the function? What about the space of time between the functions call and the first instruction of the function?
Thanks!
Short answer; use ARC.
Joe's answer is more or less correct. Until it isn't.
In general, there is no need to retain arguments or return values from other methods. However, the resulting code only works by coincidence and convention, not by algorithmic analysis.
Consider:
NSString *foo = [aMutableArray objectAtIndex: 5];
[aMutableArray removeObjectAtindex: 5];
[someTextField setTextValue: foo];
BOOM!
Your code just crashed. Maybe (it won't crash if foo happens to be a constant string or happens to have been retained by something else or happens to have been retain/autoreleased somewhere else).
Technically, that should be:
NSString *foo = [aMutableArray objectAtIndex: 5];
[foo retain];
[aMutableArray removeObjectAtindex: 5];
[someTextField setTextValue: foo];
[foo release];
That is, foo should be retained the moment it comes into scope and released the moment it is no longer used in a scope. Or you could [[foo retain] autorelease];, but autorelease pressure can be a problem (it generally isn't, but it can be).
ARC does this kind of analysis and would ensure that foo is retained as shown above when necessary.
You do not have to worry about the object being passed going away so there is no need to retain it. Proper memory management* ensures that the object will live for the duration of your method because it will be within the same thread as the caller, therefore the autorelease pool for that thread should not be drained and the caller can not release the object until your method has returned. This even holds true with methods such as performSelectorInBackground because that will retain the argument.
*Proper memory management - This means each thread that uses auto released objects gets it own autorelease pool that is drained within the same context it is created and objects passed across threads are properly retained.

Obj-c autorelease a variable up a chain of methods

I'm new to Obj-C and I have a question concerning the autorelease. Is it ok to return an autoreleased variable for several methods? For example:
- (void) methodC {
Object anObj = [self methodB];
//Do something with anObj
}
- (Object *) methodB {
return [self methodA];
}
- (Object *) methodA {
Object anObj = [[anObj alloc] init];
release [anObj autorelease];
}
Will the variable remain valid even if it is returned up a method chain and used at the top? Or does it have to be retained somewhere along the way?
thank you
Yes, it will be valid in this case. You only have to worry about the variable being deallocated if somebody drains the autorelease pool. As long as you've written every function that returns along the way and you don't explicitly drain the autorelease pool, you don't have to worry about objects being deallocated from under you.
In the vast majority of cases, the code in the NSRunLoop takes care of draining the autorelease pool. When you return control from your application code to the API code (such as by returning from a touchesBegan handler etc.), you don't know if the autorelease pool will be drained, so you have to assume in the worst case that it will. In that case, you have to retain any objects you want to keep references to.
For example:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Object *anObj = [self methodC]; // Your methodC as before -- anObj is valid
[pool drain]; // anObj will be deallocated here
The variable should remain valid. You only need to retain an object if it is actually "owned" by some other object and could be indirectly/unintentionally released along with it. For example, if you extracted an object from an array and then released the array, your object reference could become invalid unless you explicitly retain it.
For more details, see Object Ownership and Dismissal, particularly the sections on Autorelease and Validity of Shared Objects. The latter uses the following code to illustrate how you could "accidentally" make an object reference invalid.
heisenObject = [array objectAtIndex:n];
[array removeObjectAtIndex:n];
// heisenObject could now be invalid.
The following code shows how to mitigate this problem using retain.
heisenObject = [[array objectAtIndex:n] retain];
[array removeObjectAtIndex:n];
// use heisenObject.
[heisenObject release];

Objective-C memory management: how do you release the memory when you return an allocated instance?

How do you release the memory in this situation? Do you have to?
- (NSString *) whatHappensHere {
NSMutableString * mutableString = [[NSMutableString alloc] initWithString:#"Hello"];
// ....
// more code ...
// ...
return mutableString;
}
With autorelease
- (NSString *) whatHappensHere {
NSMutableString * mutableString = [[NSMutableString alloc] initWithString:#"Hello"];
[mutableString autorelease];
return mutableString;
}
As willcodejavaforfood said, the convention is that any newly-allocated object returned from a method should be autorelease'd before being returned. This tells the Objective-C runtime that if nobody takes ownership of it with retain before (in most cases) the current iteration of the application event loop ends, it should be freed.
If it's just used locally in the calling function or returned up the stack, that works great and it gets freed sooner or later. If someone ends up wanting to keep it around, then they have to retain it and so will know they have to release it themselves later on.
(In fact, most of the non-init* utility constructors for ObjC base classes like strings, etc, do just that, or at least something functionally equivalent.)

Releasing objects returned by method

Ok, I know the answer to this question should be obvious, but I need a little push in the right direction.
I find myself writing a fair number of methods that follow the following pattern:
-(NSThing*)myMethod{
NSThing *thing = [[NSthing alloc] init];
// do some stuff with the thing
return thing;
}
My question is, how do I handle the release of this object? Clearly I can't release it within the method.
usually you would autorelease it
-(NSThing*)myMethod{
NSThing *thing = [[NSthing alloc] init];
// do some stuff with the thing
return [thing autorelease];
}
Autoreleasing is the easy way to get out of this, as newacct said. However, you should take into consideration the "ownership" of the object you're returning.
In Objective-C, the general rule of thumb is that any method with alloc, new, or copy in its name returns an object that is not autoreleased, while other methods (like class methods) pre-autorelease the object to be returned. So these three are not equivalent in terms of autorelease (although the compiler may do some magic and reduce them all to string constants anyway):
// Autoreleased
NSString *string1 = [NSString stringWithString:#"aString"];
NSString *string2 = [[[NSString alloc] initWithString:#"aString"] autorelease];
// NOT autoreleased
NSString *string3 = [[NSString alloc] initWithString:#"aString"];
Your code can take a similar approach, where you consider who owns the object you're returning. In the specific example you provided, the method is the one allocing and initing the object, so in general you're responsible for autoreleaseing [sic] it within your own method. However, if you were to write a method that takes a preexisting object and modifies it in some way, you would not own that object and would not be responsible for autoreleasing the returned object. (Doing so could actually cause problems down the road when the autorelease pool to which the object belongs gets drained.)
See also (thanks to Peter and Quinn in the comments!):
Memory Management Rules