Main menu:

Site search

 

November 2017
M T W T F S S
« Oct    
 12345
6789101112
13141516171819
20212223242526
27282930  

Categories

Archives

Links:

Going fullscreen in Cocoa - Part III

Time for a completely different approach. This time when I start the fade out to black, I create a borderless window with the same dimensions as the screen I want to fade. This window has a black background but with an initial alpha value of 0.0 (so it’s not visible yet). Let’s call this a curtain window which act as a layer above anything else that might be on the screen. During the fading period, I increase the alpha value in small steps to 1.0 so that the black background of the curtain window becomes gradually visible thus obscuring everything below it. During the reverse fade, I change the alpha value back to 0.0 again in small steps and at the end I simply remove the curtain window.

During the fade process, I also hide the cursur using

[NSCursor hide];

and

[NSCursor unhide];

This is the final fade code:

NSWindow    *curtainWindow;

...

- (void)fadeInDisplay:(NSScreen*)theScreen
             fadeTime:(double)fadeTime
{
    int     fadeSteps       = 100;
    double  fadeInterval    = (fadeTime / (double) fadeSteps);

    int     step;
    double  fade;

    if ([self curtainWindow] != nil)
    {
        for (step = 0; step < fadeSteps; step++)
        {
            fade = 1.0 - (step * fadeInterval);
            [[self curtainWindow] setAlphaValue:fade];

            NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval];
            [[NSRunLoop currentRunLoop] runUntilDate:nextDate];
        } /* end for */
    } /* end if */
    [[self curtainWindow] close];
    [self setCurtainWindow:nil];

    [NSCursor unhide];
} /* end fadeInDisplay:fadeTime: */

- (void)fadeOutDisplay:(NSScreen*)theScreen
              fadeTime:(double)fadeTime;
{
    int     fadeSteps       = 100;
    double  fadeInterval    = (fadeTime / (double) fadeSteps);

    int     step;
    double  fade;

    [NSCursor hide];

    [self setCurtainWindow:[[NSWindow alloc]
        initWithContentRect:[theScreen frame]
                  styleMask:NSBorderlessWindowMask
                    backing:NSBackingStoreBuffered
                      defer:YES
                     screen:theScreen]];

    [[self curtainWindow] setAlphaValue:0.0];
    [[self curtainWindow] setBackgroundColor:[NSColor blackColor]];
    [[self curtainWindow] setLevel:NSScreenSaverWindowLevel];

    [[self curtainWindow] makeKeyAndOrderFront:nil];
    [[self curtainWindow] setFrame:[[self curtainWindow]
        frameRectForContentRect:[theScreen frame]]
                           display:YES
                           animate:NO];

    for (step = 0; step < fadeSteps; step++)
    {
        fade = step * fadeInterval;
        [[self curtainWindow] setAlphaValue:fade];

        NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval];
        [[NSRunLoop currentRunLoop] runUntilDate:nextDate];
    } /* end for */
} /* end fadeOutDisplay:fadeTime: */

For me, this works a lot better than changing the gamma values as in part II of the series. The screen fades smoothly to black without becoming first brighter as it happened when I change the gamma values of a screen. And I don’t have to worry about that kCGErrorNoneAvailable error code.

And here you can download a small sample project: goFullScreen_part_III_usingLayer.zip

Going fullscreen in Cocoa - Part II

In part I I described the the easiest solution of going full screen using the fade effect I wanted. However it had a couple of problems:

    The enterFullScreenMode and exitFullScreenMode methods as they are implemented in Leopard (OS X 10.5) hide the dock and menubar for you but no means to show the menubar again when the mouse hits the top of the screen (Snow Leopard fixes this).

    CGDisplayFade() unfortunately fades all displays which isn’t what I wanted (only the screen where the window is located should go through the fade in/out cycle).

Avoiding enterFullScreenMode and exitFullScreenMode

The trick I used is creating a new, borderless, window which will take over the full screen. I use the SetSystemUIMode() API to hide both the dock and menubar (and by specifying kUIOptionAutoShowMenuBar the menubar will appear again when the mouse hits the top of the screen). The SetSystemUIMode() function is part of the carbon framework. But don’t be afraid, this function is safe to use in a 64 bit application.

Remark: If you’re using a document based cocoa application you may consider creating a new NSWindowController for the borderless window.

- (IBAction)toggleFullscreen:(id)sender
{
    [self fadeOutDisplay:CGMainDisplayID()
                fadeTime:1.0];

    if ([self fullScreenWindow] != nil)
    {
        [[self fullScreenWindow]
            setFrame:[window contentRectForFrameRect:[window frame]]
             display:YES
             animate:NO];

        [window setContentView:[fullScreenWindow contentView]];
        [window makeKeyAndOrderFront:nil];

        [[self fullScreenWindow] close];
        [self setFullScreenWindow:nil];

        SetSystemUIMode(outMode, outOptions);
    }
    else
    {
        GetSystemUIMode(&outMode, &outOptions);
        SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);

        [self setFullScreenWindow:[[NSWindow alloc]
           initWithContentRect:[window contentRectForFrameRect:[window frame]]
                     styleMask:NSBorderlessWindowMask
                       backing:NSBackingStoreBuffered
                         defer:YES
                        screen:[window screen]]];

        [[self fullScreenWindow] setContentView:[window contentView]];
        [[self fullScreenWindow] makeKeyAndOrderFront:nil];
        [[self fullScreenWindow] setFrame:[[self fullScreenWindow]
           frameRectForContentRect:[[window screen] frame]]
                                  display:YES
                                  animate:NO];
    } /* end if */

    [self fadeInDisplay:CGMainDisplayID()
               fadeTime:1.0];
} /* end toggleFullscreen */

Fading one screen only

Apple realized that fading all screens at once might not be desirable to everyone and presents a workaround for this which can be found at the same URL (in the “Fading a single display” section).

The workaround involves finding the gamma formula of the display that you want to fade and adjust it in small steps during the fading period. The code for this is shown here:

- (void)fadeOutDisplay:(CGDirectDisplayID)display
              fadeTime:(double)fadeTime
{
    int     fadeSteps       = 100;
    double  fadeInterval    = (fadeTime / (double) fadeSteps);

    int             step;
    double          fade;
    CGGammaValue    redMin,   redMax,   redGamma,
                    greenMin, greenMax, greenGamma,
                    blueMin,  blueMax,  blueGamma;

    CGGetDisplayTransferByFormula(display,
                                  &redMin,   &redMax,   &redGamma,
                                  &greenMin, &greenMax, &greenGamma,
                                  &blueMin,  &blueMax,  &blueGamma);
    for (step = 0; step < fadeSteps; step++)
    {
        fade = 1.0 - (step * fadeInterval);
        CGSetDisplayTransferByFormula(display,
                                      redMin,   fade*redMax,   redGamma,
                                      greenMin, fade*greenMax, greenGamma,
                                      blueMin,  fade*blueMax,  blueGamma);

        NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval];
        [[NSRunLoop currentRunLoop] runUntilDate:nextDate];
    } /* end for */
} /* fadeOutDisplay */

- (void)fadeInDisplay:(CGDirectDisplayID)display
             fadeTime:(double)fadeTime
{
    int     fadeSteps       = 100;
    double  fadeInterval    = (fadeTime / (double) fadeSteps);

    int             step;
    double          fade;
    CGGammaValue    redMin,   redMax,   redGamma,
                    greenMin, greenMax, greenGamma,
                    blueMin,  blueMax,  blueGamma;

    CGGetDisplayTransferByFormula(display,
                                  &redMin,   &redMax,   &redGamma,
                                  &greenMin, &greenMax, &greenGamma,
                                  &blueMin,  &blueMax,  &blueGamma);
    for (step = 0; step < fadeSteps; step++)
    {
        fade = (step * fadeInterval);
        CGSetDisplayTransferByFormula(display,
                                      redMin,   fade*redMax,  redGamma,
                                      greenMin, fade*greenMax, greenGamma,
                                      blueMin,  fade*blueMax,  blueGamma);

        NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval];
        [[NSRunLoop currentRunLoop] runUntilDate:nextDate];
    } /* end for */
} /* end fadeInDisplay */

Compared to the code Apple presented, I don’t use usleep() to halt the application until it’s time to change the gamma table with the next values in the for loop. The simple reason is that using usleep() blocks your application making it unresponsive to any GUI updates that might be needed (e.g. redrawing a view during the fading). Instead, I used the runUntilDate: method on the currentRunLoop so that the view can e.g. be redrawn while the screen is fading (with some changes the fade in and out methods could be made fully asynchronous and might be neater approach).

However, the least I can say is that I’m not happy with the results.

    First of all, it doesn’t seem to work reliably. It might work on one Mac but not on another. E.g. on my MBP it will happily fade to black but never return back to the original state. However, if I connect my external 23 inch ACD and use that as my main screen, I get the desired effect.

    Sharp eyes may notice that there are no error checks on the returned error codes of CGGetDisplayTransferByFormula(). Depending on the Mac you use, this function can return an kCGErrorNoneAvailable error. Weird enough you will get usable CGGammaValue values even when a kCGErrorNoneAvailable is returned. I wrote usable values, because they seem to be somewhat off. In my case, when I fade to black the screen starts a bit brighter before it gradually fades to black giving a kind of a flash effect. Not nice.

In part III I’ll present a different solution on how to obtain the desired fading effect.

Here is a link to the small test project I used: goFullScreen_part_II_viaWindow.zip

Going fullscreen in Cocoa - Part I

For a project I’m working on I thought it would be nice to give the user the possibility to use the full screen (e.g. full screen editing in iPhoto, slideshow in Preview, …). As usual this turned out to be a bit less evident than I thought it would be.

The effect I wanted to achieve is as follows. When the user selects the “Go Fullscreen” menu item, the screen would fade out to black and then fade in with the key window now occupying the full screen. Selecting “Go Fullscreen” again would fade out the screen to black and then fade in with the key window having its original size again. A small sample project with the solution presented in part I can be found at the end of this blog post.

First approach:

The NSView class has the following two methods which look very promising:

- (BOOL)enterFullScreenMode:(NSScreen *)screen withOptions:(NSDictionary *)options

and

- (void)exitFullScreenModeWithOptions:(NSDictionary *)options

The possible values that can be given to the options parameter is unfortunately somewhat limited (especially in Leopard 10.5). The biggest drawback in my opinion is that you can’t animate how the view takes over the full screen (e.g. through a fading effect like I wanted to achieve, growing/shrinking the view to/from fullscreen, …). So I need some extra code to fade in and out.
The following code snippet presents the ‘core’ of going full screen and returning back.

if ([aView isInFullScreenMode] == NO)
{
    [self fadeOut];
    [aView enterFullScreenMode:[NSScreen mainScreen]
                   withOptions:nil];
    [self fadeIn];
}
else
{
    [self fadeOut];
    [aView exitFullScreenModeWithOptions:nil];
    [self fadeIn];
} /* end if */

Apple calls this the kiosk mode and has recently released some extra info which might be useful if you’re using Snow Leopard. This documentation can be found here.

Fading the screen

Apple has some documentation on how to achieve the fading effect I wanted and it can be found at this URL. The code is quite simple as can be seen here.

CGDisplayFadeReservationToken    token;  /* Fade out/in token */

...

- (void)fadeOut
{
    CGDisplayErr    err;

    err = CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval,
                                          &token);
    if (err == kCGErrorSuccess)
    {
        CGDisplayFade(token,
                      1.5,
                      kCGDisplayBlendNormal,
                      kCGDisplayBlendSolidColor,
                      0,
                      0,
                      0,
                      TRUE);
    } /* end if */
} /* end fadeOut */

- (void)fadeIn
{
    CGDisplayErr    err;

    err = CGDisplayFade(token,
                        1.5,
                        kCGDisplayBlendSolidColor,
                        kCGDisplayBlendNormal,
                        0,
                        0,
                        0,
                        TRUE);
    if (err == kCGErrorSuccess)
    {
        CGReleaseDisplayFadeReservation(token);
    } /* end if */
} /* end fadeIn */

Drawbacks of this approach

As it turns out, there are a few drawbacks with the combination of both techniques which made this a less than perfect solution for me:

    The enterFullScreenMode and enterFullScreenMode methods as they are implemented in Leopard (OS X 10.5) hide the dock and menubar for you but no means to show the menubar again when the mouse hits the top of the screen (Snow Leopard fixes this).

    CGDisplayFade() unfortunately fades all displays connected to your Mac, which isn’t what I wanted (only the screen where the window is located should go through the fade in/out cycle).

I made a small project that displays a photo of Monument Valley I took the week after WWDC ‘09. Using command-F you can toggle back and forth to full screen mode via a fade out to black and fade in again.

In part II, I’ll present another approach on how to achieve the wanted effect in an attempt to avoid the above mentioned drawbacks.

Here is project using the above code: gofullscreen-part-i.zip

Yellow or Orange breakpoints in Xcode

Way too often when I start debugging, the breakpoints I’d set suddenly change from their normal bright blue into orange (like in the screenshot below) and the debugger never stopped at all where I wanted making debugging a frustrating experience.

OrangeBreakpoints.png

Sometimes cleaning all targets and rebuilding helped to bring back my breakpoints but that workaround didn’t always work for me and after a while they turned back into orange. Searching around a bit there seems to be a workaround that actually works (at least for me). Go into the Xcode preferences, select the debugging tab and remove the check on “Load symbols lazily”.

LoadLazily.png

That did it for me.

Layer Backed Views and improving drawing speed

A few evenings ago I watched the “Session 401 - Leveraging Cocoa’s Layer-Backed Views” from WWDC 2008 again. Watching it, I concluded that just adding this line of code for every NSView could give a nice speed boost in drawing the content of the NSView.

[self setWantsLayer:YES];

To find out, I wrote a simple test application with 2 NSView’s overlaying each other. The view in the back would show a random NSBezierPath while the view on top would simply move a cursor line from left to right (very similar to what you would find in an audio editing application).

layerbackedviewapp1.png

A simple checkbox allows to switch on and off the layers for the NSView. A simple CPU load measurement is part of the application (simply the ratio between the cpu time spend in the application and the real time itself).

To let the CPU sweat, the cursor line is updated every 10 milliseconds. As a result the underlaying NSView representing the ‘wavefile’ is forced to be redrawn also. Since I don’t take care of the rectangle which needs to be updated, the full view is simply redrawn which puts a huge burden on the CPU.

On my MBP, dual core 2.5 GHz with a GeForce 8600M GT GPU this results in an average of 30% CPU load. When I now switch on the layer for every view, this is halved to 15%. This seems a nice speed boost for just one line of code to me.

Now the caveat. This only seems to work well on Mac’s which have a real GPU. Weird enough, testing the same application on a Mac Mini (dual Core 2Ghz but using the GMA950 Intel integrated graphics chip), the CPU load on the Mac Mini was only 10% without the layer, rising to 15% when I switched the layer on. So your mileage may vary…

And here is the small test project: layeredBackViews.zip

Switching spelling languages

If you’re like me, then you probably write one email in language X and another one in language Y and you’re getting tired of always switching the spell checker language to stop every word being underlined in red. That means pressing command + shift + “:”, selecting the correct language and then close the spell check window since it consumes precious screen real estate.

So of to Google to find a simple utility which could switch languages faster and a bit more user friendly. Disappointingly, I couldn’t find any. The closest I found (although I have to admit I didn’t search very long) was using a applescript on MacOSXhints.

Having a bit of free time during the Christmas festive season, I decided to make such a utility myself. This shouldn’t be hard.

I first tried if I could achieve something by using the NSSpellChecker API. I had some hopes that I could set my preferred language system wide by using this API. However it quickly became clear that making any changes using the NSSpellChecker API stayed local to my own application and that I couldn’t use this API to change the language of e.g. mail.app. Bummer.

So I took a look again at the applescript solution and decided to make a standalone CoCoa application around it. As it turned out, this introduced some new problems.

The applescript solution as presented in the MacOSXhints forum hardcodes the 2 languages you want to switch to and you always need run the applescript which seemed hardly a user improvement to me. What I wanted was a small utility that nested itself in the status bar allowing to quickly switch to another language using a drop down menu. To make it a bit more user friendly, the last 2 languages I switched to, would automatically become the first 2 entries in the menu.

spellSwitcherUtility.png

The first problem I encountered was the strange difference between the display names of the languages as they were returned by OS X and the shown languages in the spelling window.

displayNames.png

For the applescript to work correctly, you really need to tell the popup menu to select e.g. “Australian English” instead of “English (Australia)”. A simple solution could be to use a simple lookup table. However that would open another can of worms. Depending on the language you are using OS X, the name of language in use will be localized to your preferred language. E.g “Australian English” would become “Australisch Engels” in Dutch. To make it even more fun. Suppose you’re writing an email using the Dutch language setting of OS X then you would indeed see the Dutch localized display name (”Australisch Engels”). However, open the spelling window in e.g. Xcode and you will see the English display name (”Australian English”) simply because Xcode hasn’t been localized for Dutch. Trying to solve this becomes a small nightmare if you want to support a lot of languages.

I found a rather simple workaround for this. Looking more closely to the menu in the above screenshot, you’ll see that the order of the languages stays the same. If I select item 1, I will always select “Australian English” no matter what language my OS X is running at that moment.

Another [related] problem then pops up. The applescript looked for a window with the title “Spelling and Grammar”. This is fine when you only use the English version of OS X, but in other languages, this window gets a localized name and the applescript doesn’t work anymore. Below you can see the same window for Dutch, English, French and German (the 4 most used languages in Belgium, in alphabetical order).

Spelling en grammatica.png

Spelling and Grammar.png

Orthographe et grammaire.png

Rechtschreibung und Grammatik.png

So I modified the applescript to look for one of those windows and if one of them exist to perform the click on the popup menu, select the wanted language and close the window again.

Important: If the application doesn’t work, check in System Preferences -> Universal Access if support for assistive devices is allowed. Make sure that the checkbox for “Enable access for assistive devices” is checked.

AssistiveDevice.png

You can download a small Xcode project (Leopard only) which should work fine for Dutch, English, French and German localized versions of OS X. It should be straightforward to add any language you want.

This small tool is certainly not going to win any beauty contests (using applescript means that you will see the spell checker window open and close again which is a bit annoying) but it does get the job done for me.

Xcode project can be downloaded here: spellingSwitcherProject.zip
A prebuilt application can be downloaded here: spellingSwitcherApplication.zip

Shared/Dynamic libraries in OS X

A while ago I struggled with the problem of building a shared library (also known as dynamic library), let it link to a GUI build on top of it and let it be part of a clickable application. There doesn’t seems to be much information on the Internet, so here’s my story on how I achieved this using a very simple example.

The completed application

The final application will be a very simple GUI converter where you enter an angle in degrees (instead of the more usual radian) and let it calculate the sine and cosine of that angle. Calculating the sine and cosine will be done by code in a minimal mathematical shared library (note that the sin and cos functions provided by the C math library expects input in radian not in degrees, thats why I make my own small mathematical library).

sharedLibExampleFinishedApplication.png

My Mathematical library

Well, as you can guess, the code is straightforward. For the CoCoa project, we’ll have to keep the header file so that we can distribute it together with our library. The actual source code doesn’t have to be present in your project since you’re going to link against the library.
So, here’s the header file:

//
//  myMathLibrary.h
//  sharedLibsExample
//
//  Created by Koenraad Van Nieuwenhove on 06/12/08.
//  Copyright 2008 CoCoa Crumbs. All rights reserved.
//

float sine(float angleInDegrees);
float cosine(float angleInDegrees);

And here’s the implementation:

//
//  myMathLibrary.c
//  sharedLibsExample
//
//  Created by Koenraad Van Nieuwenhove on 06/12/08.
//  Copyright 2008 CoCoa Crumbs. All rights reserved.
//
#include "myMathLibrary.h"
#include <math.h>

float convertToRadian(float angleInDegrees)
{
    return (angleInDegrees*3.14159265)/180;
} /* end convertToRadian */

float sine(float angleInDegrees)
{
    return sin(convertToRadian(angleInDegrees));
} /* end sine */

float cosine(float angleInDegrees)
{
    return cos(convertToRadian(angleInDegrees));
} /* end cosine */

Building the library

Instead of using the Xcode IDE, I simply compile and build the library from the command line in a terminal (as is often the case when you would compile an open source library from e.g. MacPorts). To do this, I placed the following commands in a [bash] shell script. Lets call this script buildSharedLibrary.sh To execute the bash shell script you’ll need to open a terminal, navigate to the directory containing the source code and the shell script. You then run the shell script like this:
. ./buildSharedLibrary.sh
(Note that the dots really need to be there!!!)

    1 gcc -arch i386 -arch ppc -c myMathLibrary.c
    2
    3 gcc -arch i386 -arch ppc \
    4     -dynamiclib \
    5     -o myMathLibrary.dylib \
    6     myMathLibrary.o \
    7     -install_name @executable_path/myLibs/myMathLibrary.dylib
    8 

Line 1 simply compiles and assembles the myMathLibrary.c file into an object file. Because I used the -c command line option it stops there and it doesn’t try to link (if this command line option wasn’t there you would see an error complaining that the main function is missing). So next to our source code files, you’ll now find a new file named myMathLibrary.o
The -arch i386 and -arch ppc command line options are there to tell the compiler to build for both the Intel and PowerPC architectures. The GCC tool chain is smart enough to mix both architectures in one object file. If you want to build for PowerPC only then you can drop the -arch 386 switch of course.

Lines 3 to 7 are actually one line but I split it over several lines (using the \ character) for better readability. Lets go over it line by line.
Line 3 just says again that the GCC tool chain has to do its magic for both Intel and PowerPC architectures.
Line 4 instructs the GCC tool chain that we want a dynamic library (confusingly also [and maybe better] known as a shared library).
Line 5 tells the GCC tool chain that the resulting output filename has to be myMathLibrary.dylib
Line 6 is our object file that resulted from the compilation on line 1. For this simple example I only have one object file. However, you can specify plenty of object files here. All of them will then end up in the shared library.
Line 7 was the tricky one and the main reason why I write this blog entry. We’ll see why this is needed later.

So, if everything went well, you should now have your shared library ready to be used in your application.

The Xcode project

sharedLibExampleProject.png

As can be seen in the above screenshot, the project doesn’t look that much different compared to what you are used to. Instead of the myMathLibrary.c source code you add your compiled library to the project. Compiling and linking your application will go fine.

Running the application

When you try to run the application, you will be unpleasantly surprised by this:

sharedLibExampleRunningError.png

Clearly, the application had trouble to find our library. The reason for this is quite simple. We build the library ourselves without Xcode, so we need to instruct Xcode what to do with it, which in this case means copying the library to a suitable place so that the application can find it. If we would have stayed with our source code file, Xcode would have compiled it and taken care that the resulting object code ends up in the application. Which leaves us with the question, where do we want to copy our library to?

For this small application I prefer to put the shared library inside the application itself (another option could have been to put it somewhere in the frameworks directory). If you control click on the application icon, you can see it’s contents as shown below.

sharedLibExampleShowPackage.png

sharedLibExamplePackage01.png

As you might already know, an application is nothing more than a directory with an application icon and with a different behavior when you double click on it.
As you can see, the raw executable is located in the path /Users/koen/Desktop/sharedLibsExample.app/Contents/MacOS/.
We can now use Xcode to copy our library inside the application bundle by simply adding a new build phase. So we go back to our Xcode project and click on the Targets line where we see our sharedLibsExample. Which we double click also. Now we see the 3 build phases which Xcode already made for itself.

sharedLibExampleAddBuildPhase.png

So, we now simply add a new Copy Files Build Phase:

sharedLibExampleAddBuildPhaseMyLibs.png

By default, the Destination popup will show Resources. I selected Executables for this project. Secondly, I added an [optional] path, name myLibs.
What’s going to happen now, is that, when Xcode fires this build phase, it will copy all the files which I dragged into this build phase into a folder called myLibs which will be next to my executable (that’s why I selected the Executables option from the popup menu, if I stayed with the default, the folder would have been created in the Resources folder of the bundle instead).
The only thing that remains is dragging my library to this build phase:

sharedLibExampleAddMyLib.png

When you now build the application, Xcode will create a myLibs directory next to the executable and copy our shared library in that directory.
Try running the application this time and you’ll see it now runs as expected.

But why this install_name option?

Remember that line 7 that I didn’t explain yet? Well, rebuild our library without the -install_name @executable_path/myLibs/myMathLibrary.dylib part and run it again. Strange enough, we get a very similar error.

sharedLibExampleRunningAgainError.png

If we look inside the application bundle, we see that the library is actually there. So what went wrong this time?

sharedLibExamplePackage02.png

As it turns out, our library needs to specify some extra info for the linker. Basically it needs to tell the linker where it can be found by the main application. It’s the application who loads the shared library in RAM so that it can be executed. For this, the application needs to know where it can search for this library. To make matters a bit more difficult, our application can be located everywhere on your hard disk, so specifying a fixed path at build time is impossible. Let’s take a look again to line 7:

    7     -install_name @executable_path/myLibs/myMathLibrary.dylib

On that line we see the mention of @executable_path/ At runtime, this part will be translated to the path from root up to the executable inside the application bundle. In my case this would be /Users/koen/Desktop/sharedLibsExample.app/Contents/MacOS/ To that path, we then add myLibs/myMathLibrary.dylib, so the resulting path is /Users/koen/Desktop/sharedLibsExample.app/Contents/MacOS/myLibs/myMathLibrary.dylib which is exactly the full path name where my library is located. If I would move my application to another place, the @executable_path/ takes care that the path is adapted accordingly.
When I now double click on my application icon, the application will search for my library (to be loaded in RAM) at /Users/koen/Desktop/sharedLibsExample.app/Contents/MacOS/myLibs/. Since I used @executable_path/myLibs/myMathLibrary.dylib when I build my library and Xcode copied my library in the myLibs directory, the application will find it there, load it RAM and execute my code.

Problem solved.

A sample project can be downloaded here: sharedlibsexample

A variation on NSLog()

Using NSLog() is probably the easiest and most used [first line] debuggingtool. However, the output of NSLog() was not really to my liking and I thought it could be made more useful. The output of:

NSLog(@"%s : generated = %d", _cmd, generated);

looks typically like this:

2008-11-09 19:00:41.223 CCLog[219:10b] generate: : generated = 88

It shows the date and time (with milliseconds precision) and the name of the project before it shows the text I want to print out. In this case the called method and the value of an int.

My preferred output would be capable of doing this:
1) Show on which thread CCLog() was called from.
2) Show from which sourcecode file CCLog() was called from.
3) Show the linenumber in the sourcecode file CCLog was called from.

My first preference might seem a bit weird but as a beginning developer I found it useful to have the possibility to quickly check if my code was running in the thread I intended to. CoCoa has the capability of giving a NSThread a name [on top of its threadnumber]. This can very easily be done like this:

[[NSThread currentThread] name];

A bit of searching brought me to a blog-entry of agentM which describes his own implementation of a better NSLog(). In short, his implementation is a very nice example on the usage of variadic macros and the C preprocessor. Using the C preprocessor, It’s possible to write the CCLog() class method like this:

#define CCLog(s,...)                           \
    [CCLog myLog:__FILE__                      \
      lineNumber:__LINE__                      \
          format:(s), ##__VA_ARGS__]

The above CCLog macro expands to a class method call:

[CCLog mylog:__FILE__ lineNumber:__LINE__ format:...];

At compile-time, the __FILE__ and __LINE__ arguments would be expanded to the filename and linenumber where CCLog() was called from just like I wanted. For those classes that have a methodname like ‘init’ which appear quite often, having the filename in the log makes it easy to follow which ‘init’ method from which implementation was called. The last argument is the string with our own text. Using the variadic macro (__VA_ARGS__) we can still format strings as we are used to for formating a NSString. E.g. the following is perfectly possible:

CCLog(@"%s : generated = %d", _cmd, generated);

As you can see, we can specify any number of parameters to format our textstring like we want. The processing of the variadic macro then happens like this:

va_start(listOfArguments, format);
formattedString = [[NSString alloc] initWithFormat:format
                                         arguments:listOfArguments];
va_end(listOfArguments);

The only other remaining problem is printing out the threadname. If the threadname is not set, then the threadnumber should be logged. Getting the threadname is pretty simple and can be done like this:

[[NSThread currentThread] name];

Getting to show the threadnumber was less easy. NSThread provides a method to get its name but not its number. This can be bit problematic in those cases a thread doesn’t have a name. Asking the threadname of a thread which is not named simply returns nil. In that case I simply send the output of the currentThread class message of NSThread to a NSString. That output contains the threadnumber and can be extracted with a bit of string manipulation.

The output of CCLog() now looks like this with a threadnumber:

Thread 1 | Foo.m[20] generate: : generated = 84

The output of CCLog() now looks like this with a named thread:

Foo Thread | Foo.m[22] generate: : generated = 84

The full implementation of CCLog() can be found below and a small Xcode project can be downloaded at the end of this blogentry.

As a last remark, when you define the USE_NSLOG preprocessor macro, then the familiar NSLog() will be used instead of printf() to print out the message. This means that the date, time and project name will be added in front of the threadname, filename, linenumber and the content of the logstring. The output of the same CCLog() calls as above becomes:

2008-11-09 20:37:59.177 CCLog[424:10b] Thread 1 | Foo.m[20] generate: : generated = 84
2008-11-09 20:37:59.178 CCLog[424:10b] Foo Thread | Foo.m[22] generate: : generated = 84

The header file:

//
//  CCLog.h
//  gitVersions
//
//  Created by Koenraad Van Nieuwenhove on 26/08/08.
//  Copyright 2008 CoCoa Crumbs. All rights reserved.
//
#import <Cocoa/Cocoa.h>

// idea from http://www.borkware.com/rants/agentm/mlog/

#define CCLog(s,...)                           \
    [CCLog myLog:__FILE__                      \
      lineNumber:__LINE__                      \
          format:(s), ##__VA_ARGS__]

@interface CCLog : NSObject
{
} 

+ (void)myLog:(char*)file
   lineNumber:(int)lineNumber
       format:(NSString*)format, ...;

@end /* interface CCLog */

The implementation:

//
//  CCLog.m
//  gitVersions
//
//  Created by Koenraad Van Nieuwenhove on 26/08/08.
//  Copyright 2008 CoCoa Crumbs. All rights reserved.
//
#import "CCLog.h"

#define USE_NSLOG

@implementation CCLog

+ (NSInteger)currentThreadNumber
{
    NSString    *threadString;
    NSRange      numRange;
    NSUInteger   numLength;

    // Somehow there doesn't seem to be an listOfArgumentsI call to return the
    // threadnumber only the name of the thread can be returned but this is NULL
    // if it is not set first!
    // Here is a bit of code to extract the thread number out of the string
    // an NSThread returns when you ask its description to be printed out
    // by NSLog. The format looks like:
    //     <NSThread: 0x10113a0>{name = (null), num = 1}
    // Basically I search for the "num = " substring, copy the remainder
    // excluding the '}' which gives me the threadnumber.
    threadString = [NSString stringWithFormat:@"%@", [NSThread currentThread]];

    numRange = [threadString rangeOfString:@"num = "];

    numLength = [threadString length] - numRange.location - numRange.length;
    numRange.location = numRange.location + numRange.length;
    numRange.length   = numLength - 1;

    threadString = [threadString substringWithRange:numRange];
    return [threadString integerValue];
} /* end currentThreadNumber */

+ (void)myLog:(char*)file
   lineNumber:(int)lineNumber
       format:(NSString*)format, ...
{
    va_list      listOfArguments;
    NSString    *formattedString;
    NSString    *sourceFile;
    NSString    *logString;

    va_start(listOfArguments, format);
    formattedString = [[NSString alloc] initWithFormat:format
                                             arguments:listOfArguments];
    va_end(listOfArguments);

    sourceFile = [[NSString alloc] initWithBytes:file
                                          length:strlen(file)
                                        encoding:NSUTF8StringEncoding];

    if([[NSThread currentThread] name] == nil)
    {
        // The thread has no name, try to find the threadnumber instead.
        logString = [NSString stringWithFormat:@"Thread %d | %s[%d] %@",
                    [self currentThreadNumber],
                    [[sourceFile lastPathComponent] UTF8String],
                    lineNumber,
                    formattedString];
    }
    else
    {
        logString = [NSString stringWithFormat:@"%@ | %s[%d] %@",
                    [[NSThread currentThread] name],
                    [[sourceFile lastPathComponent] UTF8String],
                    lineNumber,
                    formattedString];
    } /* end if */

#ifdef  USE_NSLOG
    NSLog(@"%@", logString);
#else
    printf("%s\n", [logString UTF8String]);
#endif /* USE_NSLOG */

    // cleanup
    [formattedString release];
    [sourceFile release];
} /* end myLog */

@end /* implementation CCLog */

A sample project can be downloaded here: CCLog sample project

Cocoa pronunciation

Probably a weird post to start this blog with, but during my visit to WWDC ‘08 and listening to a few cocoa related podcasts it dawned to me that I always wrongly pronounced cocoa (probably due to my native language which seems to prefer to speak out both the ‘o’ and ‘a’ vowels). Thanks to the wonders of speech synthesis I can easily let you hear my wrong pronunciation (by feeding it a bad transcription of cocoa) and how I should have pronounced ‘cocoa’.

This is how I used to pronounce it:cocoa - bad pronunciation

While this is the correct pronunciation:cocoa - correct pronunciation

[No, I didn't have a gender change, I preferred the female synthesis voice because the pronunciation was better.]