High Performance

Nano aims to promote high-performance development, both at run-time and during the development process.

To achieve this, it provides several design patterns that help simplify code while improving efficiency.

Interface Builder

Nano can automatically establish connections between views in a .nib file and variables within a class:

    class MyWindow : public NWindowController {
    ...
    private:
        NIB_VIEW('filt', NCheckBox,    FilterActive);
        NIB_VIEW('volu', NSlider,      FilterValue);
        NIB_VIEW('prog', NProgressBar, Progress);
    };

When the window is constructed, each NIB_VIEW declaration is used to construct an appropriate object (e.g., an NCheckBox) which is then connected to the equivalent view in the window.

Nano also supports view properties, allowing them to be configured in ways that Interface Builder does not yet support (e.g., an NIconView can be assigned a resource string, causing it to load that image from its bundle on construction).

Copy-On-Write

NCFObject provides automatic copy-on-write behaviour, allowing common objects such as strings or dictionaries to be copied extremely efficiently.

Using this system immutable objects are "copied" by incrementing their reference count, converting them to their mutable form on demand.

This allows objects to be passed around with very little overhead, as even objects passed by value are effectively passed as const references until they are actually modified.

Operators

Nano uses operator overloading to minimise the amount of code necessary for common tasks:

    void CObject::SomeMethod(const NString &theString)
    {   NString   otherString;
     
        otherString  = theString;  
        otherString += " World!";
        
        if (theString == "Hello")
            CFShowStr(otherString);
    }


    - (void) SomeMethod:(const NSString *) theString
    {   NSMutableString   *otherString = nil;
    
        otherString = [theString mutableCopyWithZone:nil];
        [otherString appendString:@" World!"];
        
        if ([theString isEqualToString:@"Hello"])
            CFShowStr((CFStringRef) otherString);
    
        [otherString release];
    }

Since Cocoa has two string classes, each variable must be explicitly declared as immutable or mutable depending on its role. Nano provides a single NString object, which converts between either type on the fly (const-ness is validated at compile-time, by the const keyword).

Although it is overwritten in this example, otherString must be explicitly initialized in Objective-C to clear the otherwise invalid pointer. NString's default constructor will automatically initialize this variable, assigning it an empty string.

NString provides '=' and '+=' operators, allowing a more natural syntax for string manipulation than explicit method calls. By inheriting from NComparable, NString also obtains the full set of comparison operators (case-insensitive or pattern-matching comparisons are also available).

By inheriting from NCFObject, NString also obtains copy-on-write behaviour for free. This means otherString does not have to duplicate theString's data until it is actually modified. In the Objective-C example, otherString must copy the data if it could potentially be modified (even if that never occurs).

Since NString provides a CFStringRef cast operator, it will automatically cast itself to a CFStringRef when passed to an API that expects this type. Objective-C does not provide cast operators, and so any type conversions must be explicit.

In the Cocoa example, otherString is a pointer which must be explicitly released to avoid a memory leak. NString's destructor will automatically release its string when it goes out of scope.

Inline Functions

Unlike Objective-C, C++ supports inline functions. This allows performance-critical code to avoid function call overhead, while still preserving type safety:

   UInt32 CountBits(const NBitVector &theBits)
   {   UInt32    n, numBits, numSet;
   
       numBits = theBits.GetSize();
       numSet  = 0;   
   
       for (n = 0; n < numBits; n++)
           {
           if (theBits.GetBit(n))
               numSet++;
           }
       
       return(numSet);
   }

Since GetBits is an inline function, this inner loop can obtain direct access to the bits in the vector without going through a function call.