简体   繁体   中英

iPhone, passing object to method(s), memory management

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?

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.

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.

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.

Another piece of code: I am still not comfortable with it:

Assuming this is part of a function.

NSArray *txtArray = [[NSArray alloc]init];

if(aTxtField.text != NULL){ NSString *aTxtFieldTxt = [[NSString alloc]initWithString:aTxtField.text]; aTxtFieldTxt = [aTxtFieldTxt stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; [aTxt appendString:waotwTxtFieldTxt]; [aTxtFieldTxt release]; txtArray = [aTxt componentsSeparatedByString:@" "]; aTxt = [[txtArray objectAtIndex:0] mutableCopy]; for(int i = 1; i < [txtArray count]; i++){
[aTxt appendString:@"+"]; [aTxt appendString:[[txtArray objectAtIndex:i]copy]]; } [txtArray release]; return aTxt; }

if(aTxtField.text != NULL){ NSString *aTxtFieldTxt = [[NSString alloc]initWithString:aTxtField.text]; aTxtFieldTxt = [aTxtFieldTxt stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; [aTxt appendString:waotwTxtFieldTxt]; [aTxtFieldTxt release]; txtArray = [aTxt componentsSeparatedByString:@" "]; aTxt = [[txtArray objectAtIndex:0] mutableCopy]; for(int i = 1; i < [txtArray count]; i++){
[aTxt appendString:@"+"]; [aTxt appendString:[[txtArray objectAtIndex:i]copy]]; } [txtArray release]; return aTxt; }

if(aTxtField.text != NULL){ NSString *aTxtFieldTxt = [[NSString alloc]initWithString:aTxtField.text]; aTxtFieldTxt = [aTxtFieldTxt stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; [aTxt appendString:waotwTxtFieldTxt]; [aTxtFieldTxt release]; txtArray = [aTxt componentsSeparatedByString:@" "]; aTxt = [[txtArray objectAtIndex:0] mutableCopy]; for(int i = 1; i < [txtArray count]; i++){
[aTxt appendString:@"+"]; [aTxt appendString:[[txtArray objectAtIndex:i]copy]]; } [txtArray release]; return aTxt; }

if(aTxtField.text != NULL){ NSString *aTxtFieldTxt = [[NSString alloc]initWithString:aTxtField.text]; aTxtFieldTxt = [aTxtFieldTxt stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; [aTxt appendString:waotwTxtFieldTxt]; [aTxtFieldTxt release]; txtArray = [aTxt componentsSeparatedByString:@" "]; aTxt = [[txtArray objectAtIndex:0] mutableCopy]; for(int i = 1; i < [txtArray count]; i++){
[aTxt appendString:@"+"]; [aTxt appendString:[[txtArray objectAtIndex:i]copy]]; } [txtArray release]; return aTxt; }

if(aTxtField.text != NULL){ NSString *aTxtFieldTxt = [[NSString alloc]initWithString:aTxtField.text]; aTxtFieldTxt = [aTxtFieldTxt stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; [aTxt appendString:waotwTxtFieldTxt]; [aTxtFieldTxt release]; txtArray = [aTxt componentsSeparatedByString:@" "]; aTxt = [[txtArray objectAtIndex:0] mutableCopy]; for(int i = 1; i < [txtArray count]; i++){
[aTxt appendString:@"+"]; [aTxt appendString:[[txtArray objectAtIndex:i]copy]]; } [txtArray release]; return aTxt; }

Would this be allright?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM