views:

1854

answers:

6

EDIT_v002

I have had a look at all the comments and I am starting to see what I should be doing. To that end I have modified my code (see below) I have changed newPath to a NSString, removed the [[alloc] init] and the end [release] as its now handled by the system. I am using stringByAppendingPathComponent, letting it add a separator between rootPath and fileName before its assigned to the NSString. It does work, and I ran it through the static analyser with no problems.

// ------------------------------------------------------------------- **
// DISC: FILEWALKER ..... (cocoa_fileWalker.m)
// DESC: List all "*.png" files in specified directory
// ------------------------------------------------------------------- **
#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSString *fileName;
    NSDictionary *attrDir;
    NSError *myError;
    NSNumber *fileSize;
    NSFileManager *manager = [NSFileManager defaultManager];
    NSString *rootPath = [@"~/Pictures/Ren/PRMan" stringByExpandingTildeInPath];
    NSString *newPath;

    NSLog(@"Home: %@",rootPath);

    for(fileName in [manager enumeratorAtPath:rootPath]){
        if ([[fileName pathExtension] isEqual:@"png"]) {    

            newPath = [rootPath stringByAppendingPathComponent:fileName];   
            attrDir = [manager attributesOfItemAtPath:newPath error:&myError];
            fileSize = [attrDir objectForKey: @"NSFileSize"];
            NSLog(@"File: %@ Size: %@", newPath, fileSize);
        }
    }
    [pool drain];
    return 0;
}
// ------------------------------------------------------------------- **

gary

A: 
[testPath stringByAppendingString:@"/"]
ennuikiller
+3  A: 

You don't append the delimiter. You append the next path component (eg filename, dir, etc). This avoids you needing to know the delimiter for your particular system.

NSMutableString* mutablePath = [NSMutableString string];
NSString* fullPath = [rootPath stringByAppendingPathComponent:filename];

[mutablePath setString:fullPath]; // OK to setString: of Mutable with non-Mutable
[mutablePath appendString:someOtherString]; // This won't cause an exception

// Example to clarify on comments below
{
    // This will cause a compiler warning.
    // warning: incompatible Objective-C types assigning
    //    ‘struct NSString *’, expected ‘struct NSMutableString *’
    NSMutableString* ms = [@"FOO" stringByAppendingPathComponent:@"BAR"];
}

There is a fairly clear example in the documentation.

nall
[testPath setString:[@"TEST_" stringByAppendingPathComponent:@"/"]]; just curious, testPath is NSMutableString, does that work in this situation?
fuzzygoat
Yes. That works fine.
nall
just curious, if something returns an NSString and you assign it to an NSMutableString I would assume that ok?
fuzzygoat
If you do that and then try to modify the string, you'll get an exception, since the immutable string won't respond to the selector from NSMutableString.
Quinn Taylor
I added some code and comments above to try to make this clearer.
nall
+1  A: 

The last line should be:

testPath = [testPath stringByAppendingPathComponent:@"/"];
André Hoffmann
This would turn `testPath` into an `NSString` when it has been declared and allocated as a mutable string.
dreamlax
Furthermore, the original `testPath` string is now leaked, since it wasn't autoreleased.
dreamlax
+2  A: 

-stringByAppendingPathComponent returns a new immutable string, it doesn't modify the original. You have to use the return value of this method.

dreamlax
+4  A: 

All the existing answers are leaking the original testPath string. For something as simple as this, why has nobody recommended -[NSMutableString appendString:] intead?

[testPath appendString:@"/"];

There's no equivalent to -stringByAppendingPathComponent: for NSMutableString, but it looks like he's just trying to add a slash, not a path component anyway. If you really wanted to add a path component, you could do this:

[testPath setString:[testPath stringByAppendingPathComponent:@"..."]];

It's an annoying workaround, but as @dreamlax points out, -stringByAppendingPathComponent: always returns an immutable string, even when called on an NSMutableString object. :-(

Quinn Taylor
NSMutableString *is* an NSString, and thus has stringByAppendingPathComponent.
nall
I understand that, but it still returns a new autoreleased string, rather than modifying the contents of the mutable string, as one might expect if there were a method called `-appendPathComponent:` as well.
Quinn Taylor
It seems like this is the simplest way to get what the original asker wanted, and have the result still be a NSMutableString.
alesplin
Ah, I misunderstood your point. We're on the same page.
nall
i suggested stringByAppendingString way back
ennuikiller
You did, but without storing the result to a variable, it doesn't fully solve the OP's problem, which was that the `testPath` wasn't modified. All in all, `-stringByAppendingString:` isn't a good solution for a mutable string.
Quinn Taylor
+2  A: 

stringByAppendingPathComponent, hows it work?

Simple. You want to append a path component. You send that message to the string you want to append a path component to, passing the path component you want to append.

Path components are not the slashes; if they were, the pathComponents method would return nothing but an array of slashes. Path components are the parts between the slashes (although there is a special case, described in the definition of pathComponents).

The slash is the path separator. This is hard-coded inside of Cocoa; it's currently (and likely to always be) a slash. So, if you really wanted to append a slash to a string, the most likely reason would be that you want to append a path separator, not a path component.

    [newPath setString:rootPath];
    [newPath appendString:@"/"];
    [newPath appendString:fileName];

fileName is the component you want to add. Use stringByAppendingPathComponent: and pass fileName, not a slash.

As for whether your example leaks: Well, does an object fall out of scope without getting released? The answer to that question is the answer to whether it's a leak. If you're not sure, review the memory management rules.

Peter Hosey
So in essence you supply the path components and stringByAppendingPathComponent adds the seperator? Also I can't see how you say it doesn't work, on my system it lists all 4 of the EXR images in the path. Basically newPath is first allocated, then simply gets reset and rebuild each time round the loop, ultimately getting release at the end.
fuzzygoat
Yes. You normally only supply one path component, but a subpath works just as well.
Peter Hosey
Also: You're correct about `newPath`. Sorry for the confusion on that; I didn't see your `setString:` message. I've deleted the paragraph, since it doesn't apply to the code in the current version of your question.
Peter Hosey
NP at all, did you see the updated version at the top. It feels right (based on my knowledge so far) and more importantly works.
fuzzygoat
I referred to it in my previous comment.
Peter Hosey