Static Inline Functions

There are times in the development cycle where any programmer would stop and accept any offer of advice or assistance to get the task at hand complete. Some of these tasks may not associated with a new feature of any sort. The type of helpers I'm referring to will enhance your current codebase as well as allow you to grow and success with future endeavors within your Xcode project.

These helpers are called Static Inline Functions and are a reliant, valuable and high-performing way of getting repetitive stuff accomplished.

What's so great about inline funtions?

Using inline functions instead of method calls gives us three (I'll start with the basics) advantages:

1) Speed. We gain performance because the we lose objc_sendMsg() method binding.
2) Simplicity. We lose the complexity of the Objective-C runtime library, e.g., IMP, SEL, etc.
3) Reliability. We gain faster address cache access when these functions are called repeatedly.

Why static inline?

There are hundreds of questons & answers on StackOverflow about static inline functions:

static inline is usually used with small functions that are better done in the calling routine than by using a call mechanism, simply because they are so short and fast that actually doing them is better than calling a separate copy. E.g.:

static inline double square(double x) { return x*x; }  

Eric had an excellent explanation of why static inline functions can be vital to your development process.

Real world applications

In the complex stack of iOS development, programmers are constantly battling thread management and concurrency. Whether handling an intense about of work on background thread to release the main thread for quick UI response, or forcing UI logic to be invoke only on the main thread to avoid exceptions and dead locking, everyone at some point understands the imporantance of thread knowledge.

The helper functions I've provided below, help us manage threading with simple helper blocks (which remove the ugliness of GCD APIs). Hopefully this will relieve some of the chaos and triple-nested block code within some implementations. I know these have helped me out tremendously.

Enjoy!

//  PXPInlineHelperFunctions.h

/**
 @brief Executes the enclosed block asynchronously on the main thread.
 @note If the current thread is the main thread, the block will be executed immediately.
 @warning The given @c block will not wait for execution to be complete before continuing
 to the rest of the code on that thread.
 */
NS_INLINE void RunOnMainThread(dispatch_block_t block) {  
    [NSThread mainThread] ? block() : dispatch_async(dispatch_get_main_queue(), block);
}

/**
 @brief Executes the enclosed block synchronously on the main thread.
 @note If the current thread is the main thread, the block will be executed immediately.
 @warning The given @c block will wait for execution to be complete before continuing
 to the rest of the code on that thread.
 */
NS_INLINE void RunOnMainThreadAndWait(dispatch_block_t block) {  
    [NSThread mainThread] ? block() : dispatch_sync(dispatch_get_main_queue(), block);
}

/// @returns @c YES if build is a debug environment
NS_INLINE BOOL IsDebugBuild() {  
    static BOOL isDebug;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
#if DEBUG
        isDebug = YES;
#else
        isDebug = NO;
#endif
    });
    return isDebug;
}

/// @returns @c YES if build is running on an iOS simulator; otherwise @c NO.
NS_INLINE BOOL IsRunningOnSimulator() {  
    static BOOL isSimulator;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
#if TARGET_IPHONE_SIMULATOR
        isSimulator = YES;
#else
        isSimulator = NO;
#endif
    });
    return isSimulator;
}

/// @returns @c YES if build is running on an iOS device; otherwise @c NO.
NS_INLINE BOOL IsRunningOnDevice() {  
    return IsRunningOnSimulator() == NO;
}

/// @returns a subview of the given @c view that is a kind of the given @c klass.
NS_INLINE id FindSubviewOfKindOfClass(UIView *view, Class klass) {  
    for (id subview in [view subviews]) {
        if ([subview isKindOfClass:klass]) {
            return subview;
        }
    }

    for (id subview in [view subviews]) {
        if ([subview isKindOfClass:[UIView class]]) {
            UIView *aSubview = (UIView *)subview;
            return FindSubviewOfKindOfClass(aSubview, klass);
        }
    }
    return nil;
}

Comments