macOS
Category | Platforms |
---|
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
- __FILE__ is defined as a string literal that contains the name of the current source file.
- __LINE__ is defined as the integer line number in the current source file.
- __FUNCTION__ is defined as a string literal that contains the undecorated name of the enclosing function.
Build
- We build an Xcode project (xcodeproj) with Cmake.
- We use the default Apple Clang compiler.
- In the top CMakeLists.txt file, we need to link with the Cocoa framework.
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.
- Create a pool (NSAutoreleasePool). Cocoa uses a reference-counted memory management system. https://developer.apple.com/documentation/foundation/nsautoreleasepool
auto pool = [[NSAutoreleasePool alloc] init];
- Create the application object (NSApplication). This object handles the main event loop and keeps track of the application window. https://developer.apple.com/documentation/appkit/nsapplication
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
- contentRect is the origin and the size of the window. On macOS, the origin of the coordinate system is at the bottom-left corner of the screen. We define a custom width and height for the window and use the frame property of the NSScreen.mainScreen value to center the window on the screen.
auto screenRect = [[NSScreen mainScreen] frame];
auto windowRect = NSMakeRect((screenRect.size.width - Width) * 0.5,
(screenRect.size.height - Height) * 0.5,
Width,
Height);
- style is the window's style. We provide options for capabilities such as a title bar, a minimize and close button and optionally a resizable border. https://developer.apple.com/documentation/appkit/nswindowstylemask
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];