Cocoa Snippet: Finding a File’s ‘Kind’
January 7, 2007 at 5:46 pm 6 comments
+ (NSString *)humanReadableFileType:(NSString *)path
{
NSString *kind = nil;
NSURL *url = [NSURL fileURLWithPath:[path stringByExpandingTildeInPath]];
LSCopyKindStringForURL((CFURLRef)url, (CFStringRef *)&kind);
return kind ? [kind autorelease] : @""; // If kind happens to be an empty string, don't autorelease it
}
Thanks very much to Peter Hosey and David Phillip Oster for being unbelievably helpful in clearing up the labyrinth that is CSStringRef to NSString usage.
1. Peter Hosey | January 7, 2007 at 10:14 pm
Don’t forget to release what you have copied. In CF, you own a reference to any object that you Create, Copy, or Retain, and you must release that reference accordingly. (For comparison, the rule in Foundation is copy, alloc, retain, or new.)
The easiest way would be to autorelease it. Thanks to toll-free bridging, autorelease works just fine for CF-obtained objects (the autorelease pool will send the object -release, which is equivalent to CFRelease).
Don’t forget to *not* autorelease your empty string, though (since it already has a net retain count of 0). The easiest way would be something like this:
LSCopyKindStringForURL(…, &kind);
if(kind) kind = [kind autorelease];
else kind = @””;
return kind;
Or, if you’re OK with the ?: operator:
LSCopyKindStringForURL(…, &kind);
return kind
? [kind autorelease]
: @””;
2. David Phillip Oster | January 12, 2007 at 5:40 am
Your example is wrong. You must write it this way:
+ (NSString *)humanReadableFileType:(NSString *)path{
NSString *kind = nil;
NSURL *url = [NSURL fileURLWithPath:[path stringByExpandingTildeInPath]];
LSCopyKindStringForURL((CFURLRef)url, (CFStringRef *)&kind);
return kind ? [kind autorelease] : @””;
}
That way, you get the toll-free bridging from CSStringRef to NSString, and you either return the autoreleased copy, or nil.
My point is that the line:
NSString *kind = [NSString string];
is wrong, since one of two things will happen:
(a) LSCopyKindStringForURL() will ignore the previous value of ‘kind’ and store over it with its own value, a copy that must be autoreleased.
or
(b) LSCopyKindStringForURL() will hit an error executing, and potentially do nothing.
in case (a), you must autorelease the result, in case (b) you must not autorelease the result. So the only reasonable initial value for kind is nil, since [nil autorelease] is harmless to execute.
3. David Phillip Oster | January 14, 2007 at 1:10 am
I was overly sharp in my previous comment. I apologize for my tone.
[NSString string] returns an autoreleased object. If you autorelease it again, you’ve set up a double-delete when the pool is drained.
#import
#if DEBUG // debug builds only
NSDebugEnabled = YES;
NSZombieEnabled = YES;
#endif
will cause the runtime system to get your attention if you do a double delete.
4. David Phillip Oster | January 14, 2007 at 1:10 am
should be:
#import <NSDebug,h>
5. Patrick | January 14, 2007 at 1:18 am
Thank you very much, David. I truly appreciate – and am rather startled – that people read my blog and are passionate enough to correct me when I make errors.
Do you have a blog?
6. David Phillip Oster | January 15, 2007 at 8:39 am
I’m glad you asked, since this gives me an opportunity to paste, out of working code:
#import <Foundation/NSDebug.h>
#if DEBUG
NSDebugEnabled = YES;
NSZombieEnabled = YES;
#endif
No, I don’t yet have a blog, just 9,600 hits on
http://groups.google.com/groups/search?q=%22david+phillip+oster%22&start=0&scoring=d