<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xml:base="http://www.potionfactory.com" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
 <title>Cocoa</title>
 <link>http://www.potionfactory.com/taxonomy/term/35/feed</link>
 <description>The taxonomy view with a depth of 0.</description>
 <language>en</language>
<item>
 <title>F#$% Nil Outlets</title>
 <link>http://www.potionfactory.com/blog/2008/06/15/f-nil-outlets</link>
 <description>&lt;p&gt;This is for you cocoa developers out there.&lt;/p&gt;

&lt;p&gt;A common error I keep making is forgetting to set an outlet in Interface Builder. Usually I catch the problem pretty quickly but once in a blue moon it can leave me scratching my head for too long a period of time to confess here.&lt;/p&gt;

&lt;p&gt;I love problems that can be engineered away and after making this mistake yet again, I decided to solve it once and for all. I&#039;m sharing the solution because it has worked out splendidly.&lt;/p&gt;

&lt;p&gt;The following code parses your header file for outlets and makes sure that they are set at runtime.&lt;/p&gt;
&lt;!--break--&gt;

&lt;p&gt;In a header file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#ifndef RELEASE
#define PFValidateOutlets(obj) PFValidateOutlets_(obj, [NSString stringWithFormat:@&quot;%s&quot;, __FILE__], nil)
#define PFValidateOutletsInFile(obj, header) PFValidateOutlets_(obj, [NSString stringWithFormat:@&quot;%s&quot;, __FILE__], header)
#else
#define PFValidateOutlets(obj)
#define PFValidateOutletsInFile(obj, header)
#endif
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In a .m file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#import &amp;lt;objc/runtime.h&amp;gt;

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

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

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

        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&#039;s class
            if ([scanner scanUpToCharactersFromSet:wordTokenizingSet intoString:&amp;className] &amp;&amp;
                [className isEqualToString:NSStringFromClass([obj class])]) {

                NSArray *lines = [classSource componentsSeparatedByCharactersInSet:lineSeparatingSet];

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

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

&lt;p&gt;Usage:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- (void)awakeFromNib
{
    PFValidateOutlets(self);
    // Rest of the code
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see, I&#039;m invoking the validation manually in &lt;code&gt;awakeFromNib&lt;/code&gt; of classes that have outlets. It would be awesome if there were way to make the check fully automatic but I stopped investigating because this is good enough for me. Let me know if you think of something though.&lt;/p&gt;

</description>
 <comments>http://www.potionfactory.com/blog/2008/06/15/f-nil-outlets#comments</comments>
 <category domain="http://www.potionfactory.com/blog/archives/cocoa">Cocoa</category>
 <pubDate>Sun, 15 Jun 2008 22:46:49 -0700</pubDate>
 <dc:creator>Andy Kim</dc:creator>
 <guid isPermaLink="false">231 at http://www.potionfactory.com</guid>
</item>
<item>
 <title>How to Keep Backwards Compatibility and be Less Grumpy About It</title>
 <link>http://www.potionfactory.com/blog/2007/11/04/how-keep-backwards-compatibility-and-be-less-grumpy-about-it</link>
 <description>&lt;p&gt;I dread backwards compatibility. I dread it because it means that for every product that I need to keep backwards compatible, the testing burden is at least twice as big. The math is simple: testing on two major OS versions and two processor types can be as much as 4 times of stuff I&#039;d rather not be doing.&lt;/p&gt;
&lt;p&gt;The last time I had to maintain a product with backwards compatibility, this is what I used to do:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Build then copy application to test machine.&lt;/li&gt;
&lt;li&gt;Get it to fail. This is the one part where I don&#039;t have to work very hard.&lt;/li&gt;
&lt;li&gt;Try to fix while scratching head then proceed to littering print statements all over the code.&lt;/li&gt;
&lt;li&gt;Go back to step 1.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can make this process a little less sucky by configuring Xcode to copy after each build, or by mounting a share so that there&#039;s no copying at all. But in the end, debugging with print statements alone is limiting and time consuming. There has to be a better way, right?&lt;!--break--&gt;&lt;/p&gt;
&lt;p&gt;The better way turns out to be handsomely better and it&#039;s called remote debugging. A remote debugger is usually used for kernel hacking or for developing full-screen applications such as games, but it&#039;s quite handy for fixing backward compatibility problems—especially if your app no longer builds on Tiger.&lt;/p&gt;
&lt;p&gt;With remote debugging, I can code in Xcode 3—my new favorite IDE—then just hit command-Y. It works as any great software should. Like magic. The application launches on the test machine and you&#039;re off debugging as if it were running locally. You can set breakpoints, examine variables and stack traces, fix and continue, or whatever else that gdb can do. There&#039;s no longer a cycle of doom, and as a result, I&#039;m a much happier person.&lt;/p&gt;
&lt;p&gt;There are just 3 things you need to do to get stress free debugging on older systems with Xcode:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set up password-less ssh for logging into your test machine.&lt;/li&gt;
&lt;li&gt;Share the build directory in a way that both machines can access it through the same path&lt;/li&gt;
&lt;li&gt;Configure a new executable in Xcode for remote debugging.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Xcode User Guide does a fine job of explaining these steps succinctly.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://developer.apple.com/documentation/DeveloperTools/Conceptual/XcodeUserGuide/Contents/Resources/en.lproj/06_02_db_set_up_debug/chapter_42_section_7.html#//apple_ref/doc/uid/TP40002699-SW28&quot;&gt;Xcode User Guide — Debugging Programs Remotely&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Some additional tips:&lt;/p&gt;
&lt;p&gt;1. If your test machine has a different kind of processor, duplicate your Debug build configuration and set it up to build a universal binary.&lt;/p&gt;
&lt;p&gt;2. Use &quot;DWARF with dSYM File&quot; as your debug metadata format. With the other two options, breakpoints set through the Xcode UI didn&#039;t work for me.&lt;/p&gt;
</description>
 <comments>http://www.potionfactory.com/blog/2007/11/04/how-keep-backwards-compatibility-and-be-less-grumpy-about-it#comments</comments>
 <category domain="http://www.potionfactory.com/blog/archives/cocoa">Cocoa</category>
 <category domain="http://www.potionfactory.com/blog/archives/software-development">Software Development</category>
 <category domain="http://www.potionfactory.com/blog/archives/tips">Tips</category>
 <pubDate>Sun, 04 Nov 2007 03:35:01 -0800</pubDate>
 <dc:creator>Andy Kim</dc:creator>
 <guid isPermaLink="false">215 at http://www.potionfactory.com</guid>
</item>
<item>
 <title>Quick NSDate Warning</title>
 <link>http://www.potionfactory.com/blog/2007/08/30/quick-nsdate-warning</link>
 <description>&lt;p&gt;This is a quick warning for Cocoa programmers.&lt;/p&gt;
&lt;p&gt;The following piece of code will not trigger an assertion error:&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;
NSDate *now = [NSDate date];&lt;/p&gt;
&lt;p&gt;#if defined(__i386__)&lt;br /&gt;
NSAssert([now laterDate:nil] == now, @&quot;now should be later than nil&quot;);&lt;br /&gt;
#else&lt;br /&gt;
NSAssert([now laterDate:nil] == nil, @&quot;nil should be later than now&quot;);&lt;br /&gt;
#endif&lt;br /&gt;
&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I think that the correct return value is &lt;em&gt;now&lt;/em&gt; since any date is more recent than no date at all, but I admit that the logic is dubious. Best thing to do is to make sure that nil does not get passed in to the date comparison methods at all.&lt;/p&gt;
&lt;p&gt;This is the reason why Tangerine! version 1.2.3&lt;strong&gt;b&lt;/strong&gt; has to exist. Don&#039;t let it get you too.&lt;/p&gt;
&lt;p&gt;UPDATE (December 12, 2007): This is fixed in Leopard now.&lt;/p&gt;
</description>
 <comments>http://www.potionfactory.com/blog/2007/08/30/quick-nsdate-warning#comments</comments>
 <category domain="http://www.potionfactory.com/blog/archives/cocoa">Cocoa</category>
 <pubDate>Thu, 30 Aug 2007 07:47:45 -0700</pubDate>
 <dc:creator>Andy Kim</dc:creator>
 <guid isPermaLink="false">200 at http://www.potionfactory.com</guid>
</item>
</channel>
</rss>
