Return to Snippet

Revision: 8539
at September 26, 2008 20:33 by zingo


Initial Code
// -------------------------
// header file
// -------------------------
#ifndef RELEASE
#define PFValidateOutlets(obj) PFValidateOutlets_(obj, [NSString stringWithFormat:@"%s", __FILE__], nil)
#define PFValidateOutletsInFile(obj, header) PFValidateOutlets_(obj, [NSString stringWithFormat:@"%s", __FILE__], header)
#else
#define PFValidateOutlets(obj)
#define PFValidateOutletsInFile(obj, header)
#endif

// -------------------------
// .m file
// -------------------------
#import <objc/runtime.h>

void PFValidateOutlets_(id obj, NSString *filepath, NSString *headerFilename)
{
#ifndef RELEASE
    if (headerFilename) {
        filepath = [[filepath stringByDeletingLastPathComponent] stringByAppendingPathComponent:headerFilename];
    }
    if (![filepath hasSuffix:@".h"]) {
        filepath = [NSString stringWithFormat:@"%@.h", [filepath stringByDeletingPathExtension]];
    }

    // Parse the outlets
    if ([[NSFileManager defaultManager] fileExistsAtPath:filepath]) {
        NSString *source = [NSString stringWithContentsOfFile:filepath];
        NSArray *classes = [source componentsSeparatedByString:@"@interface"];

        NSCharacterSet *spacesSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
        NSCharacterSet *wordTokenizingSet = [NSCharacterSet characterSetWithCharactersInString:@" \t\n\r{}*:;"];
        NSCharacterSet *statementTokenizingSet = [NSCharacterSet characterSetWithCharactersInString:@"{}:;/"];
        NSCharacterSet *lineSeparatingSet = [NSCharacterSet characterSetWithCharactersInString:@"\n\r"];

        for (NSString *classSource in classes) {
            NSScanner *scanner = [NSScanner scannerWithString:classSource];
            [scanner scanCharactersFromSet:spacesSet intoString:nil];

            NSString *className;

            // Proceed if the parsed class matches the object's class
            if ([scanner scanUpToCharactersFromSet:wordTokenizingSet intoString:&className] &&
                [className isEqualToString:NSStringFromClass([obj class])]) {

                NSArray *lines = [classSource componentsSeparatedByCharactersInSet:lineSeparatingSet];

                for (NSString *line in lines) {
                    // Skip comment lines
                    if ([[line stringByTrimmingCharactersInSet:spacesSet] hasPrefix:@"//"]) continue;

                    NSArray *statements = [line componentsSeparatedByCharactersInSet:statementTokenizingSet];
                    for (NSString *statement in statements) {
                        if ([[statement stringByTrimmingCharactersInSet:spacesSet] hasPrefix:@"IBOutlet"]) {
                            NSString *outletName = [[statement componentsSeparatedByCharactersInSet:wordTokenizingSet] lastObject];
                            Ivar outletIvar = class_getInstanceVariable([obj class], [outletName UTF8String]);
                            id outletValue = object_getIvar(obj, outletIvar);
                            if (outletValue == nil)
                                NSLog(@"WARNING -- %@'s outlet %@ is nil", className, outletName, outletValue);
                        }
                    }
                }
            }
        }
    }
#endif
}

// -------------------------
// usage
// -------------------------
- (void)awakeFromNib
{
    PFValidateOutlets(self);
    // Rest of the code
}

Initial URL
http://www.potionfactory.com/blog/2008/06/15/f-nil-outlets

Initial Description


Initial Title
Parse Header File for Outlets and makes sure that they are set at Runtime

Initial Tags
debug, osx

Initial Language
Objective C