Posts with tag 'Cocoa'

One element of making a solid application is to make it as easy as possible for users to report problems to you. But, it doesn't help you the developer much if you get a report such as "I received an error 42 when I tried to load my file." Wouldn't it be better to have the error report tell you the exact line of code.

I'm going to show you how to extract a stack trace from an NSException. Apple has kindly posted a guide to printing symbolic stack traces, however, their solution requires that the Developer Tools be installed because it uses the atos command-line utility.

The method I will outline below does not require any external command-line tools which may-or-may-not be installed. Instead, they require a couple of glibc functions.

And, just to get fancy, this code will also de-mangle C++ symbols in your stack traces. Those who are working in Objective-C only (lucky bastards) can delete half of the code with no repercussions.

This is the sort of problem best described with code, so here goes:

NSString * UTstackTraceForException(NSException * exception)
{
    NSArray * addresses = [exception callStackReturnAddresses];
    void * backtrace_frames[[addresses count]];
    int i = 0;
    NSString * trace = @"";
    NSString * frame_description, * symbol;

    for (NSNumber * address in addresses)
    {
        backtrace_frames[i] = (void *)[address unsignedLongValue];
        i++;
    }

    char **frameStrings = backtrace_symbols(&backtrace_frames[0], [addresses count]);

    if(frameStrings != NULL) {
        int x;
        for(x = 0; x < [addresses count]; x++) {
            // Loop over the description of each stack frame.
            // Try to find the symbol name and demangle it if possible

            if(frameStrings[x] == NULL) { break; }

            char * demangled = NULL;
            size_t length;
            int status;

            frame_description = [NSString stringWithUTF8String:frameStrings[x]];
            NSArray * components = [frame_description componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

            int count = 0;
            for (NSString * component in components)
            {
                if ([component length] == 0)
                    continue;

                count++;
                if (count == 4)
                {
                    symbol = component;
                    demangled = __cxxabiv1::__cxa_demangle([symbol UTF8String], 
                                                           demangled, 
                                                           &length, &status);
                    break;
                }
            }

            if (demangled)
            {
                NSString * demangled_string = [NSString stringWithUTF8String:demangled];
                frame_description = [frame_description stringByReplacingOccurrencesOfString:symbol withString:demangled_string];
                free(demangled);
            }

            trace = [trace stringByAppendingFormat:@"%@\n", frame_description];
        }
        free(frameStrings);
    }
    return trace;
}

I made my rookie podcast appearance on Late Night Cocoa a few days ago. My episode is called Porting Large Scale Applications to the Mac Platform (Note that one of the definitions of "episode" in my dictionary is "A finite period in which someone is affected by a specified illness").

During the episode I ramble on about many of the things I learned the hard way while working on the port of Maya, and more recently, the port of Houdini to the Mac platform.

I think I'm the first new "premium content" on the Mac Developer Network now Scotty has changed up the paid content model. I'm flattered, but hope he's got other plans to drive subscriptions.

Speaking of that, go out and support the MDN! It's a great service that I'd hate to lose.

Nokia has just announced that Qt v4.5 is going to be available in LGPL form. This is a huge announcement, and is interesting from a number of perspectives, many of which aren't necessarily obvious at first glance.

I was a recent guest on Late Night Cocoa where I extolled the virtues of developing applications in high level languages like Python, especially if you wish to do cross-platform development. Nokia's announcement could be a game changer.

I love Cocoa and the new Python bindings that Apple has supplied. However, if you need to do things cross-platform, Qt is the best alternative. I have concluded, from my many experiments, that Qt is the only cross-platform toolkit that approaches the polish of Cocoa. And, RiverBank's excellent PyQt is a very natural binding for Python. And, best of all, Qt 4.5 on the Mac is built on Cocoa.

However, up until now there has been a problem. If you licensed Qt for use in a commercial app, you would be forbidden to directly expose Qt via a scripting language to your customers. This makes sense from a certain perspective because a development license for Qt can run up to $3k. If you offer scripting in your app, and your app costs less than a $3k, then you would be undercutting Nokia. And, you would be keeping all the money.

In other words, until now you would have been prohibited from using PyQt in your commercial application if you exposed a scripting API, even if you were willing to pay the licensing fees for Qt and PyQt. However, with the LGPL, you'll be able to link to the Qt libraries from a commercial app without a commercial license from Nokia.

If people realize the inherent value in using Qt with a scripting language like Python, this could open up a whole new avenue for low-cost, high-quality, cross platform development.

Lets see if we can kickstart this blog with a quickie Cocoa tip. You know it's a bad sign that I always have to look up Markdown syntax every time I post because I've forgotten since the last time.

In Snow Leopard, Apple added some enumeration methods to several collection classes that use blocks. Several of those use a mysterious NSEnumerationOptions parameter. There aren't any links in the docs to the valid flag values for NSEnumerationOptions parameters.

NSArray: -enumerateObjectsWithOptions:usingBlock:, -enumerateObjectsAtIndexes:options:usingBlock:
NSDictionary: -enumerateKeysAndObjectsWithOptions:usingBlock:
NSSet: -enumerateObjectsWithOptions:usingBlock:
NSIndexSet: -enumerateIndexesWithOptions:usingBlock:, -enumerateIndexesInRange:options:usingBlock:

For anyone else Googling for some answers, I did eventual dig up the documentation for the values.

NSEnumerationConcurrent: invoke the Block on the selected elements concurrently; the order of invocation is nondeterministic and undefined; this flag is a hint and may be ignored by the implementation under some circumstances; the code of the Block must be safe against concurrent invocation

NSEnumerationReverse: invoke the Block on the selected elements in reverse of the natural order; available for NSArrays and NSIndexSets; undefined for NSDictionarys and NSSets, or when combined with the NSEnumerationConcurrent flag

Now, I'll go log a RADAR bug about the missing link...

Disclaimer: The above statements and opinions are my own and do not represent those of my employer.