Retaining elements in NSArray

I'm creating a NSMutable array in the following way:

-(NSArray*)createArrayFromString:(NSString*)str
{
    NSArray *arr = [str componentsSeparatedByString:@" "]; 
    NSMutableArray *result = [NSMutableArray arrayWithCapacity:[arr count]];

    for(NSString *s in arr){
        if([s length]>0){
            [result addObject:[s retain]];
        }
    }
    return (NSArray*) result;
}

Here's the calling method, which puts the received array elements into the property resRefs of the Chapter object:

-(Chapter*)createChapter:(CXMLNode*)node
    {
        Chapter *chapter = [[Chapter alloc] init];
        chapter._id = [[(CXMLElement*)node  attributeForName:@"id"] stringValue];
        chapter.title = [[(CXMLElement*)node attributeForName:@"title"] stringValue];
        chapter.text = [node stringValue];
        [chapter.pids addObjectsFromArray:[self createArrayFromString:[[(CXMLElement*)node attributeForName:@"pids"]stringValue]]];
        [chapter.resRefs addObjectsFromArray:[self createArrayFromString:[[(CXMLElement*)node attributeForName:@"resRefs"]stringValue]]];
        return chapter;
    }

Here's the question:

Without the retain call in

[result addObject:[s retain]];

I can't access the NSString elements from my ViewController (EXC_BAD_ACCESS)

Am I correctly using retain here?

EDIT:

Changed

 [[result addObject:s] retain];

into

 [result addObject:[s retain]];

The 2nd line was originally in my code. Removed retain and inserted it back into the wrong place when posting this question. Still, having the retain here works, while leaving it out doesn't.

EDIT 2:

Discovered the Analyzer (CMD+SHIFT+B), found a couple of memory leaks, removed them and the EXC_BAD_ACCESS went away. Thanks to all of you for your help!

Answers


[result addObject:s]

returns nothing, so there is nothing to retain.

the object s gets retained by the array.

NSMutableArray *result = [NSMutableArray arrayWithCapacity:[arr count]];

wont get retained, and shouldn't, as it is a local object.

You should familiarize yourself with the memory rules. teh naming conventions say, that a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy”, must deliver a retaind object. So your method just seems to be fine, as it doesnt start with any of it, and returns a autoreleased (=unretained) object. It is the callers duty, to retain or not.

Also a side note:

return (NSArray*) result;

wont transform your NSMutableArray into a NSArray. A cast only tells the compiler, what it should expect. as a NSMutableArray is a NSArray, the compiler already knows that. And actually the cast is already defined by the return definition in the method's signature.


Are you doing something else in the method? if not — I don't want to distress you — you really don't need it, as NSArray *arr = [str componentsSeparatedByString:@" "]; just does what you want. If you really want to factor the @" " away from the caller, you should consider using a category on NSString called something like componentsSeparatedByBlank.

it could look like

@interface NSString (Separation)
-(NSArray *)componentsSeparatedByBlank;
@end

@implementation NSString (Separation)
-(NSArray *)componentsSeparatedByBlank
{
    return [self componentsSeparatedByString:@" "];
}
@end

or with a white spaces character set:

-(NSArray *)componentsSeparatedByWhiteSpace
{
    NSArray *array = [self componentsSeparatedByCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
    return [array filteredArrayUsingPredicate: [NSPredicate predicateWithFormat:@"SELF != ''"]];
}

Am I correctly using retain here?

No. NSMutableArray retains objects that you add to it. It then releases them when you call remove or when the array is dealloc'd. There is not need for you to call retain here. However, this isn't why you are experiencing a EXC_BAD_ACCESS

As vikingosegundo said, addObject doesn't return anything, so calling retain on it gives you EXC_BAD_ACCESS.

The correct syntax for what you are trying to do is (NOTE: there is no need to do this as explained above)

[result addObject:[s retain]]; // BAD

So, in conclusion, just

[result addObject:s]; // GOOD - s is retained by result

EDIT * Rewritten posters original code...

-(NSArray*)arrayFromString:(NSString*)str // Note naming convention of method
{
    NSMutableArray *arr = [str componentsSeparatedByString:@" "]; // Won't give you any 0 length strings

// Do some other character/validation checks here?

    return arr; // returns autoreleased NSMutableArray complying to naming convention
}

If you don't want to do anything with the strings before returning them you could just use this in your createChapters method...

NSArray *arr = [[[(CXMLElement*)node attributeForName:@"pids"] stringValue] componentsSeparatedByString:@" "];
[chapter.pids addObjectsFromArray:arr];

Please read up on memory management and naming conventions, will save you a lot of time in the future.


[[result addObject:s] retain]; 

is a void method. This means that it does not return any value like:

- (NSString)methodThatReturnsString;

would. You are retaining a void, that does not retain anything, because void means nothing basically. The object you add the array to is retained by the array however.


Need Your Help

ddply skip if nrow = 1

r plyr skip

I have a ddply that goes over a list of IPs and applies a fun over each IP. I want the fun to return a value only if the nrow(ip.data) > 1. Otherwise, I want ddply to skip over that IP and conti...