views:

81

answers:

4

Hey guys, I know there are a lot of questions on this topic already, but it is just not clear to me yet. So, what I am still wondering about is, if I call a method and pass it an object, do I then have to retain this object inside my method to use it there. And if I retain it, where do I release it. Lets make a bit of a more complex example. Could someone explain whats wrong with this:

NSString *myStr = [[NSString alloc]initWithString:@"Hello"];

myStr = [self modString2:[self modString1:myStr]];
[myStr release];

//These are the methods...

-(NSMutableString*)modString1:(NSString*)str{
 return [[[str stringByAppendingString:@" Mr. Memory"] mutableCopy] autorelease];
}

-(NSMutableString*)modString2:(NSString*)str{
 return [[[str stringByAppendingString:@" How about this?"] mutableCopy] autorelease];
}

This is so confusing to me. Lets assume I create an object inside a method:

[self modString:[self createString]];


-(NSString*)createString{
 NSString *string = [NSString stringWithString:@"Hello"];
 return string;
}

-(NSMutableString*)modString:(NSString *)str{
 [str retain];
 NSMutableString *mut = [NSMutableString stringWithString:str];
 return mut;
}

Would this be correct? Another thing: If I copy a string from an array into a string like:

NSString *str = [NSString alloc[ initWithString:[[arr objectAtIndex:0]copy]]];

does the retain the whole array, or whats happening here? Would that mean I have to release the array? I dont get it. Are there any practical resources apart from apple`s? I really want to understand this...

A method does not own an object which is getting passed to it as an argument?! Right? And I only would have to retain an object in a method, if the object itself is an object returned by a method (which was called before) with an autorelease via: return [object autorelease] and therefore was created within the method, which was called at first.

And another one:

For example if I do the following:

request = [[NSMutableURLRequest alloc] initWithURL:url];

can I then release the url after this, or does it still have to stick arround for the request to be valid?

A: 

If you only need it inside your method, you don't need to retain it.
If you need to store it in an instance variable for use later, the retain it, and release it in the dealloc method, or when you assign a new object to the instance variable.

Examples concerning my comment

If you modify the value of the string (NSMutableString), you don't have to worry. You just need to release it in the method which created the string. You may have problems if you return a pointer to another string, and if you assign this new string to the previous one. In such a case, you can't access the original pointer anymore, and you have a memory leak as you can't release it anymore

Example 1

{
    NSArray * arr = [ [ NSArray alloc ] initWithObject: @"Foo", @"Bar", nil ];
    [ self someMethod: arr ];
    [ arr release ];
}
- ( void )someMethod: NSArray * arr
{
    arr = [ NSArray emptyArray ];
}

This is ok, no memory leak, even if you assign another array in someMethod, because the pointer is local to the method, and it won't affect the original pointer.

Example 2

{
    NSArray * arr = [ [ NSArray alloc ] initWithObject: @"Foo", @"Bar", nil ];
    [ self someMethod: &arr ];
}
- ( void )someMethod: NSArray ** arr
{
    *( arr ) = [ NSArray emptyArray ];
}

Here, we have a memory leak, as we modify the original pointer. Note we used **, meaning we have a pointer to a pointer.

Example 3

{
    NSArray * arr = [ [ NSArray alloc ] initWithObject: @"Foo", @"Bar", nil ];
    arr           = [ self someMethod: arr ];
}
- ( NSArray * )someMethod: NSArray * arr
{
    return [ NSArray emptyArray ];
}

Memory leak, as we've redefined the pointer to the arr array. It has been allocated, but we can't release it since the pointer points to another variable.

Macmade
Well, lets say I alloc and init a String, then pass it from one method to another. I dont have to worry about the string in the methods, even though they expect the string modify it and return it?
jagse
If you modify the value of the string (NSMutableString), you don't have to worry. You just need to release it in the method which created the string. You may have problems if you return a pointer to another string, and if you assign this new string to the previous one. In such a case, you can't access the original pointer anymore, and you have a memory leak as you can't release it anymore.
Macmade
So I should not be doing something like the following:replace = @"xyz";cleanString = [[cleanString stringByReplacingOccurrencesOfRegex:replace withString:@"zzz"]mutableCopy];Would this be what you have described?
jagse
See the edit : )
Macmade
Ok, thanks a lot. What still is not clear. If I assign a string which is stored in an array to a NSString *pointer = [arr objectAtIdex:3];can I then release the array afterwards or does it need to be retained? If I copy the string, can I then release it?NSString *pointer = [[arr objectAtIndex:3]copy];
jagse
When you copy an object, you own the copy. So you need to release it at some point. If you get a value from an array, you don't need to release it as long as you don't retain it.
Macmade
Basically: retain -> release / alloc -> release / copy -> release. Otherwise, don't. But be careful with pointer (re)assignation, as in my examples.
Macmade
Yes, but can I release the array without braking my pointer to this object? It does not make sence to me to keep an array in memory just because I have a pointer to one object of the array
jagse
In such a case, if you release the array, the objects contained will also be released. So you'll have a problem. In such a case, retain the string. Then you can release the array safely.
Macmade
Would it also be correct to copy it like:NSMutableString *author = [[NSString alloc]initWithString:[[txtArray objectAtIndex:0] mutableCopy]];[txtArray release];
jagse
If you copy it, there's also no problem since it's a different instance that you own.
Macmade
A: 

I'm going to try and explain pointers, sorry if you already know this.

Most objc objects are pointers. You can tell because they they have a * at the end of the object name. NSString is not a pointer, but NSString* is a pointer.

An NSString pointer (NSString*) object contains only the address to a space in memory where that string is stored. When you create a pointer object you need to ask the computer for space to store your object. In objc you do this by calling the static alloc method. So

NSString* s = nil; //s contains NOTHING
s = [[NSString alloc] stringWithString:@"hello"]; //space in memory is created for s, s contains the address to that memory. That memory block now holds "hello".

Now, your computer wants you to tell it if you won't use that memory block anymore. That is when you call

[s release];

to let your computer know that it can take over that memory block (I assume you know about the counter-style memory management that objc works with). If you try to access that memory block AFTER it's no longer yours, then thats when you get all those fun memory errors.

Now when you call a method that requires you to pass an object like

[foo doSomethingWith:s];

What you're actually passing isn't the object "hello", you're passing the pointer to the address in memory that holds "hello". This way of doing things comes in handy when you have a HUGE data structure (like an array of ints of size 1,000,000). Instead of passing the huge array into a function, you just pass the pointer of that array. This is faster and more efficient.

So when should you release allocated objects? You usually want to release them when you no longer need them, but in the same function in which you allocated them. If they are instance variables then you allocate in the init function of your class and release in the dealloc function. In many cases you don't need to retain / copy things, so unless you're having memory errors I wouldn't worry about those.

I hope this helps XD If any of the info isn't accurate for objc, I'm sorry. I learned memory management with C++ and it's totally different.

jmont
+1  A: 

In your first block of code, you are never really modifying the value of the string myStr, the returned values from the method is just tossed out. If you modify the line to read like this:

    NSString *myStr2 = [self modString1:[self modString2:myStr]];

The string myStr2 will have the value of "Hello How about this? Mr. Memory", and it will be an autoreleased object, which you do not have to release.

Also, keep in mind that when you add an object to a mutable array, the object is automatically retained, so that you can release it after adding it to the array, and the object will stay alive until the object is removed from the array or the array is released.

BP
Sorry, just edited my code block. Wanted it to be like it is now...
jagse
And then my question would be if I would have to release the str inside the method cause of the mutableCopy and if the autorelease does make sence anyway...
jagse
In general, you do not want to call release on an autorelease object, as it can crash the application with the dreaded EXC_BAD_ACCESS signal. I try not to mix autorelease and retained objects, it can get confusing in a hurry.
BP
A: 
jagse
Or would this be like the 3rd. array example?
jagse