Mac OS X Reference Library Apple Developer
Search

Using SDK-Based Development

This chapter describes SDK-based development techniques to use in your projects, explaining how your code can tell what version of a framework it is using at run time, check for undefined method and functions, compile conditionally for different SDKs, and find deprecated APIs.

Determining the Version of a Framework

There are several ways to check at run time for availability of certain features in the versions of the frameworks your application is linked against. You can dynamically look for a given class or method (for example, by using the instancesRespondToSelector: method, as described in “Checking for Undefined Method and Function Calls”) and, if it isn’t there, follow a different code path. Another way is to use global framework-version constants, if they are provided by the framework.

For example, the Application Kit (in NSApplication.h) declares the NSAppKitVersionNumber constant, which you can use to detect different versions of the Application Kit framework:

APPKIT_EXTERN double NSAppKitVersionNumber;
#define NSAppKitVersionNumber10_0 577
#define NSAppKitVersionNumber10_1 620
#define NSAppKitVersionNumber10_2 663
#define NSAppKitVersionNumber10_2_3 663.6
#define NSAppKitVersionNumber10_3 743
#define NSAppKitVersionNumber10_3_2 743.14
#define NSAppKitVersionNumber10_3_3 743.2
#define NSAppKitVersionNumber10_3_5 743.24
#define NSAppKitVersionNumber10_3_7 743.33
#define NSAppKitVersionNumber10_3_9 743.36
#define NSAppKitVersionNumber10_4 824
#define NSAppKitVersionNumber10_4_1 824.1
#define NSAppKitVersionNumber10_4_3 824.23
#define NSAppKitVersionNumber10_4_4 824.33
#define NSAppKitVersionNumber10_4_7 824.41
#define NSAppKitVersionNumber10_5 949
#define NSAppKitVersionNumber10_5_2 949.27
#define NSAppKitVersionNumber10_5_3 949.33

You can compare against this value to determine which version of the Application Kit your code is running against. One typical approach is to floor the value of the global constant and check the result against the constants declared in NSApplication.h. For example:

if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_0) {
  /* On a 10.0.x or earlier system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_1) {
  /* On a 10.1 - 10.1.x system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_2) {
  /* On a 10.2 - 10.2.x system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_3) {
  /* On 10.3 - 10.3.x system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_4) {
  /* On a 10.4 - 10.4.x system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_5) {
  /* On a 10.5 - 10.5.x system */
} else {
  /* 10.6 or later system */
}

Similarly, Foundation (in NSObjCRuntime.h) declares the NSFoundationVersionNumber global constant and specific values for each version.

Some individual headers for other objects and components may also declare the version numbers for NSAppKitVersionNumber where some bug fix or functionality is available in a given update.

Checking for Undefined Method and Function Calls

To run successfully, your code must avoid calling methods and functions in system versions that do not support them. It can do this either by checking the system version at run time and globally taking a different code path based on the version, or by checking for the existence of each Objective-C method or C function before calling it.

For example, suppose you build your application to use features in Mac OS X version 10.5 (by setting the Base SDK to that version) but still run on Mac OS X version 10.4 (by setting the Deployment OS version to that version). In Objective-C, you use the instancesRespondToSelector: method to see if the method selector in question is available. For example, to use the setDisplaysToolTips: method, first available in version 10.5, you could use code like the following:

Listing 3-1  Checking for an undefined method

if ([NSTextView instancesRespondToSelector:@selector(setDisplaysLinkToolTips:)])
    {
        [myTextView setDisplaysLinkToolTips:NO];
    }
    else
    {
        // Code to disable link tooltips with earlier technology
    }

When your code runs on Mac OS X version 10.5, it calls setDisplaysLinkToolTips: to disable display of help tags when the mouse hovers over links. When it runs on version 10.4, it must disable display of link help tags using code you wrote for that version.

If you were to build this code with different settings, you would see the following results:

You can check for the existence of Objective-C properties by passing the getter method name (which is the same as the property name) to instancesRespondToSelector:.

In C, instead of using theinstancesRespondToSelector: method, you compare the function pointer to NULL. For example, to use the CGColorCreateGenericCMYK function, first available in version 10.5, you could use code like the following:

Listing 3-2  Checking for a null function pointer

    if (CGColorCreateGenericCMYK != NULL)
    {
        CGColorCreateGenericCMYK(0.1,0.5.0.0,1.0,0.1);
    }
    else
    {
        // Code to create a color object with earlier technology
    }

Important: When checking for the existence of a symbol, you must explicitly compare it to NULL or nil in your code. You cannot use the negation operator ( ! ) to negate the address of the symbol.

Conditionally Compiling for Different SDKs

If you build the same source code using different SDKs, you might need to compile code conditionally based on the SDK in use. You can do this by using preprocessor directives with the macros defined in Availability.h.

Note: Availability.h is for iOS and Mac OS X v10.6 and later development. AvailabilityMacros.h is the earlier version introduced in Mac OS X 10.2. These header files reside in the /usr/include directory.

For example, suppose the code shown in Listing 3-2 must be compiled against the Mac OS X 10.4 SDK. Because the code refers to the CGColorCreateGenericCMYK function (which was introduced in Mac OS X v10.5) that portion of the code must be excluded when compiling against the earlier SDK. This is because even referring to the CGColorCreateGenericCMYK function in such a case causes a compiler error. The use of the __MAC_OS_X_VERSION_MAX_ALLOWED macro removes the code during preprocessing unless it is being compiled against the 10.5 headers. Note the use of the value 1050 instead of symbol __MAC_10_5 in the #if comparison clause: if the code is loaded on a system that does not include the symbol definition, the comparison still works.

#ifdef __MAC_OS_X_VERSION_MAX_ALLOWED
    // code only compiled when targeting Mac OS X and not iPhone
    // note use of 1050 instead of __MAC_10_5
    #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
        if(CGColorCreateGenericCMYK != NULL)
        {
            CGColorCreateGenericCMYK(0.1,0.5.0.0,1.0,0.1);
        }
        else
        {
    #endif
            // code to create a color object with earlier technology
    #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
        }
    #endif
#endif
}

In addition to using preprocessor macros, the preceding code also assumes that the code could be compiled using a newer SDK but deployed on a computer running Mac OS X v10.4 and earlier. Specifically, it checks for the existence of the CGColorCreateGenericCMYK symbol before attempting to call it. This prevents the code from generating a runtime error, which can occur if you build the code against a newer SDK but deploy it on an older system. For more information about instituting runtime checks to determine the presence of symbols, see “Checking for Undefined Method and Function Calls.”

Finding Deprecated APIs

As Mac OS X and iOS evolve, the APIs and technologies they encompass are sometimes changed to meet the needs of developers. As part of this evolution, less efficient interfaces are deprecated in favor of newer ones. The availability macros help you find deprecated interfaces.

Note: Deprecation does not mean the immediate deletion of an interface from a framework or library. It is simply a way to flag interfaces for which better alternatives exists. For example, an older interface may be discouraged in favor of a newer, more efficient interface. You may still use deprecated interfaces in your code; however, Apple recommends that you migrate to newer interfaces as soon as possible because deprecated APIs may be deleted from a future version of the OS. Check the header files or documentation of the deprecated interface for information about any recommended replacement interfaces.

Each deprecated interface is tagged with a macro that identifies the version of Mac OS X in which it was marked as deprecated. If you compiled your project with a Deployment OS version of Mac OS X 10.5 and used an interface tagged in this way, you would get a warning that the interface is now deprecated. The warning includes the name of the deprecated interface and where in your code it was referenced. For example, if the HPurge function were deprecated, you would get an error similar to the following:

'HPurge' is deprecated (declared at /Users/steve/MyProject/main.c:51)

To locate references to deprecated interfaces, you can look for warnings of this type. If your project has a lot of warnings, you can use the search field in Xcode to filter the list based on the “deprecated” keyword.




Last updated: 2010-02-16

Did this document help you? Yes It's good, but... Not helpful...