Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

zeppenwolf

macrumors regular
Original poster
Nov 17, 2009
129
3
Essentially all relevant code listed below. Not much.

I wrote a class for an old project to handle accessing the "Application Support" folder; that old project had all kinds of busy-work it had to do there.

In my new project, I also want to access that folder, but it's vastly simpler-- I only need to write files there, to separate sub-folders. So I copied the source files and started whittling down until this point, where there's almost nothing left.

What is left is (at least) three lazy initializing properties. For the subfolders, the code for supportSubFolder01 and supportSubFolder02 is close to identical-- the only differences being 1) the name of the subfolder, which could be passed as a parameter, perhaps, or even an optional parameter, and 2) the name of the underlying property.

My gut tells me that a property must have a unique address, and yet... ObjC can sometimes do such queer and magic things that I can't allow myself to discount the possibility that... somehow...

It might be asking a lot, but might there be some way to have a function/whatever which can be used to create an arbitrary number of lazy properties, each of which is distinguishable just by a parameter string ( or other way of specifying string ) ?

Ermp... is it clear? Thx.

Code:
@interface AppSupportFolder : NSObject {

    @private

        // Guaranteed path props
        NSString*            _supportFolder;
        NSString*            _supportSubFolder01;
        NSString*            _supportSubFolder02;
}

// Guaranteed path props
@property ( readonly ,    nonatomic , copy )            NSString*            supportFolder;
@property ( readonly ,    nonatomic , copy )            NSString*            supportSubFolder01;
@property ( readonly ,    nonatomic , copy )            NSString*            supportSubFolder02;

@end
///
///////////////////////////// h above, m below
///
@implementation AppSupportFolder

// Guaranteed path props
//
@synthesize supportFolder            = _supportFolder;
@synthesize supportSubFolder01        = _supportSubFolder01;
@synthesize supportSubFolder02        = _supportSubFolder02;


// MARK: guaranteed path props
//
// These props not only promise the string path value, they create the underlying finder item when needed.
//
- (NSString*)        supportFolder {

    if ( _supportFolder )
        return _supportFolder;

    NSArray*    pathsArray        = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSUserDomainMask, true );

    XXErr_logIf( ! [pathsArray count] );
    if ( ! [pathsArray count] )
        return 0;

    return _supportFolder    = [[pathsArray objectAtIndex:0] retain];
}

- (NSString*)        supportSubFolder01 {

    if ( _supportSubFolder01 )
        return _supportSubFolder01;

    if ( ! self.supportFolder )
        return 0;

    _supportSubFolder01    = [[NSString stringWithFormat:@"%@/%@" , self.supportFolder , @"solved"] retain];

    if ( [[NSFileManager defaultManager] fileExistsAtPath:_supportSubFolder01] )
        return _supportSubFolder01;

    XXErr_messageWithFmtStr( @"supportSubFolder01: creation failure" );
    RELEASE_AND_ZERO( _supportSubFolder01 );
    return 0;
}

- (NSString*)        supportSubFolder02 {

    if ( _supportSubFolder02 )
        return _supportSubFolder02;

    if ( ! self.supportFolder )
        return 0;

    _supportSubFolder02    = [[NSString stringWithFormat:@"%@/%@" , self.supportSubFolder02 , @"aborted"] retain];
    if ( [[NSFileManager defaultManager] fileExistsAtPath:_supportSubFolder02] )
        return _supportSubFolder02;

    XXErr_messageWithFmtStr( @"supportSubFolder02: creation failure" );
    RELEASE_AND_ZERO( _supportSubFolder02 );
    return 0;
}
 
The simplest way is to pass in a pointer to the instance variable (NSString **), and the string to append to self.supportFolder.

The getter would look a bit like this after that change:

Code:
return [self lazySubfolder:&_supportSubFolder2 withPath:@“abort”]

You would have to add in another parameter if you wanted to log the name of the property in the refactored function.

Also, I might recommend looking at stringByAppendingPathComponent off of NSString. It does what you are trying to do with the string format code.
 
And..... IT WORKS!!!

Oh, sure... my brain felt like a pretzel for a while, but I got over that part.

Ultimately, it came out pretty simple and I'm happy with the result. I now have two classes, one a fully re-useable base class...

Code:
@interface AppSupportFolders : NSObject {

    @protected

        NSError*            error;

        NSString*            _sysAppSupportFolder;
        NSString*            _appSuppMyHomeFolder;
}

@property ( readonly ,    nonatomic , copy )            NSString*            sysAppSupportFolder;
@property ( readonly ,    nonatomic , copy )            NSString*            appSuppMyHomeFolder;

+ (AppSupportFolders*)    shared;

- (NSString*)            lazyFolderProp:(FoldPropHook)propHook inParent:(NSString*)homeFold withName:(NSString*)makeFold;

@end

...which provides lazy & "guaranteed" so to speak properties for .../Application Support/ & .../Application Support/MyKillerApp & creates the folder if needed. Not the most awesome of all code, perhaps, but nice and frequently useable maybe. BUT the base class also contains the Secret Sauce function lazyFolderProp: which facilitate the second class, inheriting, which is all about MyKillerApp specifically. In the inheriting class, lazy guarantees are reduced to one line:

Code:
- (NSString*) suppSubFold_Solved {
    return [self lazyFolderProp:&_suppSubFold_Solved inParent:self.appSuppMyHomeFolder withName:kSolvedName];
}

Well... hope that was clear. Thanks Kevnik for the clever bit, I'm pleased with the result. Here's the base class m file just for the record:

Code:
//
//  AppSupportFolders.m

#import "AppSupportFolders.h"

#import "XXError.h"
#import "release_and_delete.h"


@implementation AppSupportFolders

// Guaranteed path props
//
@synthesize sysAppSupportFolder        = _sysAppSupportFolder;
@synthesize appSuppMyHomeFolder        = _appSuppMyHomeFolder;


// MARK: shared, dealloc
//
static AppSupportFolders*    sInstance    = 0;

+ (AppSupportFolders*) shared {

    static dispatch_once_t onceToken;

    dispatch_once( &onceToken, ^{
        sInstance = [[AppSupportFolders alloc] init];
    });

    return sInstance;
}

- (void)            dealloc {

    RELEASE_AND_ZERO( _sysAppSupportFolder );
    RELEASE_AND_ZERO( _appSuppMyHomeFolder );

    [super dealloc];
}

// MARK: guaranteed path props
//
// These next funcs not only promise the string path value, they create the underlying finder item when needed.
//
- (NSString*)    lazyFolderProp:(FoldPropHook)propHook inParent:(NSString*)homeFold withName:(NSString*)makeFold {

    if ( *propHook )
        return *propHook;

    if ( ! homeFold )
        return 0;

    *propHook        = [[homeFold stringByAppendingPathComponent:makeFold] retain];

    error= 0;
    BOOL    success    = [[NSFileManager defaultManager]
                createDirectoryAtPath:            *propHook
                withIntermediateDirectories:    YES
                attributes:                        nil
                error:                            &error];

    if ( ! success ) {
        if ( error )
            XXErr_logNSErrWithFmtStr( error , @"lazyFolderProp: creation failure for %@" , makeFold );
        else
            XXErr_messageWithFmtStr( @"lazyFolderProp: creation failure for %@" , makeFold );
        RELEASE_AND_ZERO( *propHook );
        return 0;
    }

    return *propHook;
}

// This lazy guarantees .../Application Support/
//
- (NSString*)        sysAppSupportFolder {

    if ( _sysAppSupportFolder )
        return _sysAppSupportFolder;

    NSArray* pathsArray = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSUserDomainMask, true );

    XXErr_logIf( ! [pathsArray count] );
    if ( ! [pathsArray count] )
        return 0;

    return _sysAppSupportFolder    = [[pathsArray objectAtIndex:0] retain];
}

// This lazy guarantees .../Application Support/MyKillerApp/
//
- (NSString*)        appSuppMyHomeFolder {

    if ( _appSuppMyHomeFolder )
        return _appSuppMyHomeFolder;

    // Guarantee that the parent folder .../Application Support/ exists & is a property
    //
    if ( ! self.sysAppSupportFolder )
        return 0;

    NSString*    bundleName    = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];

    return [self lazyFolderProp:&_appSuppMyHomeFolder inParent:self.sysAppSupportFolder withName:bundleName];
}

@end
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.