🍎

macOS

CategoryPlatforms

Overview

macOS applications are built using the Cocoa API.

Cocoa relies heavily on the late binding capabilities of Objective-C for many of its core technologies. Therefore, it's more practical to use Objective-C or Swift.

Swift has no C++ interoperability yet but Objective-C was designed to mix with C and C++. Therefore, we will use Objective-C to implement Cocoa-related functionalities.

Xcode and Clang

C++

noinline

https://gcc.gnu.org/onlinedocs/gcc-4.4.5/gcc/Function-Attributes.html

The noinline attribute tells the compiler to never inline a particular member function.

__attribute__((noinline))

always inline

https://gcc.gnu.org/onlinedocs/gcc/Inline.html

https://gcc.gnu.org/onlinedocs/gcc-4.4.5/gcc/Function-Attributes.html

The always_inline attribute overrides the cost/benefit analysis and ensure the member function will be inlined.

__attribute__((always_inline))

Predefined Macros

https://gcc.gnu.org/onlinedocs/gcc/Function-Names.html

Build

target_link_libraries(Legend "-framework Cocoa")

Debugger

To generate a signal SIGSEGV, we can invoke an assembly interruption.

__asm {int 3};
__asm__("int3");

Alternatively, we can call the abort function from stdlib. http://www.cplusplus.com/reference/cstdlib/abort/

void abort(void);

Entry Point

Most macOS applications are built using Xcode and NIB files and all the initialization code for AppKit is executed behind the scene.

void NSApplicationMain(int argc, char *argv[])
{
    [NSApplication sharedApplication];
    [NSBundle loadNibNamed:@"main" owner:NSApp];
    [NSApp run];
}

We will not use NIB files and write all the initialization in Objective-C++.

It's possible to mix Objective-C++ and C++ code in the same Objective-C++ file (mm).

The entry point of the application is the main() C function.

int main(int argc, const char* argv[])
{
}

Initialization

We need to handle the following initialization steps.

auto pool = [[NSAutoreleasePool alloc] init];
auto application = [NSApplication sharedApplication];

Application Delegate

Create and assign the application delegate (NSApplicationDelegate). https://developer.apple.com/documentation/appkit/nsapplicationdelegate

Some methods are implemented to respond to events, such as initialization or shutdown.

@interface ApplicationDelegate: NSObject<NSApplicationDelegate>

@end

@implementation ApplicationDelegate

-(void)applicationWillFinishLaunching:(__unused NSNotification*)notification
{
}

-(void)applicationDidFinishLaunching:(__unused NSNotification*)notification
{
}

-(void)applicationWillTerminate:(__unused NSNotification*)notification
{
}

-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(__unused NSApplication*)sender
{
    return YES;
}

-(void)handleQuit:(__unused id)sender
{
    [[NSApplication sharedApplication] terminate:nil];
}

@end

We then create and assign the delegate to the applicati.

[application setDelegate:[[[AppDelegate alloc] init] autorelease]];

Window

To create the main window, we need to create an instance of NSWindow. https://developer.apple.com/documentation/appkit/nswindow

auto window = [[NSWindow alloc] initWithContentRect:...];

The initWithContentRect:styleMask:backing:defer method has two important parameters. https://developer.apple.com/documentation/appkit/nswindow/1419477-initwithcontentrect

auto screenRect = [[NSScreen mainScreen] frame];
auto windowRect = NSMakeRect((screenRect.size.width - Width) * 0.5,
                             (screenRect.size.height - Height) * 0.5,
                              Width,
                              Height);
styleMask: NSWindowStyleMaskTitled |
           NSWindowStyleMaskClosable |
           NSWindowStyleMaskMiniaturizable |
           NSWindowStyleMaskResizable

Window Delegate

We define and implement a custom window delegate (NSWindowDelegate). https://developer.apple.com/documentation/appkit/nswindowdelegate

Some methods are implemented to respond to events, such as window resizing or closing.

@interface WindowDelegate: NSObject<NSWindowDelegate>

@end

@implementation WindowDelegate

- (NSSize)windowWillResize:(NSWindow*)window
                    toSize:(NSSize)frameSize
{
    return frameSize;
}

- (void)windowWillClose:(id)sender
{
}

@end

We then create and assign the delegate to the window.

[window setDelegate:[[[WindowDelegate alloc] init] autorelease]];

Main Loop

Then, we start the main event loop of the application.

[application run];

Shutdown

Finally, when we leave the event loop and the applications is shutting down, we release the pool.

[pool release];