Enhanced ObjC Coding Style Guide (Version 1.0)

For a compact summary, see the Enhanced ObjC Coding Style Cheat Sheet.

1. Scope

This guide extends both the standard ObjC Coding Style Guide and the Enhanced C Coding Style Guide.

The standard ObjC guide remains authoritative for formatting, layout, line breaking, message-send style, blocks, and vertical spacing.

The enhanced C guide remains authoritative for the shared wrapper layer, especially begin_header, begin_impl, def, set, guard, require, assert, integer aliases, and pointer/nullability macros.

This guide covers only the enhanced Objective-C syntax that exists in Objective-C mode.

2. Activation and Wrapper Rules

The Objective-C helper macros are activated automatically by the normal wrapper layer.

Use the same file prologues as in enhanced C.

Examples:

#include "begin_header.h"
begin_header
// declarations
end_header
#include "end_header.h"

#include "begin_impl.h"
begin_impl
// definitions
end_impl

3. Property Attribute Macros

Prefer the ObjC property shorthand macros when they express the intended property attributes clearly.

Prefer RO and RW by default. Non-atomic properties are the project default.

Use A_RO or A_RW only when atomic semantics are intentionally required.

Combine the shorthand with ordinary property attributes where needed.

Keep all normal property layout rules from the standard ObjC guide.

Examples:

@property(RW) NSString * name;
@property(RO) NSArray <NSString *> * values;
@property(RW,weak) Opt(id <SettingsWindowControllerDelegate>) delegate;
@property(A_RO,copy) NSArray <NSString *> * allKeys;

4. Class and Member Attribute Macros

Prefer the ObjC class/member shorthands over spelling the raw attributes by hand.

Use final for classes that are not intended to be subclassed. That should be the default.

Use private_methods when members of a class or category should use direct Objective-C dispatch.

Use internal for internal leaf classes where both restrictions are wanted together.

Keep the attribute macros on their own line directly above the @interface or @implementation they modify.

Examples:

final
@interface ColoredView : NSView

internal
@interface SettingsWindowKeywordItem : NSObject <SettingsWindowTableItem>

private_methods
@implementation SettingsWindowController (Filtering)

5. Direct Method Attribute Macro

Use direct_call for individual methods when direct Objective-C dispatch is required for that specific method.

Prefer private_methods when most members of the same class or category should be direct.

Use direct_call only for isolated methods when the class as a whole should not opt into private_methods.

Keep direct_call adjacent to the method signature it modifies.

Example:

- (void)reload direct_call

6. Disabling Default Initializers

Use no_default_inits inside an interface when plain new and init must be unavailable.

Place no_default_inits inside the @interface, usually near the designated initializer declarations.

Follow it with the initializer APIs that callers are expected to use instead.

Prefer this macro when an object requires construction parameters to be valid.

Example:

@interface SettingsWindowKeywordItem : NSObject

	no_default_inits

	- (instancetype)initWithSection:
		(id <SettingsWindowSection>)section
		name:(NSString *)name;

@end

7. ObjC Object Guard Macros

The shared guard, return_unless, break_unless, and continue_unless macros work well for boolean conditions and for values that already have the desired type.

Objective-C mode also provides object-aware variants for binding maybe-nil Objective-C expressions.

Available macros:

Prefer guard_objc(...) { ... } endguard when a block should execute only if one or more maybe-nil Objective-C expressions bind successfully.

Prefer the ObjC variants when the assignment expression yields an Objective-C object that may be nil.

These macros bind assigned values as def locals and cast the bound value to a nonnull Objective-C type after the nil check succeeds.

guard_objc supports the same trailing condition and else { ... } form as the shared guard.

The *_unless_objc macros still support an optional trailing condition, just like the shared C variants.

Use the shared non-ObjC guard and *_unless macros for plain conditions, scalar values, and cases where no ObjC object cast is needed.

Examples:

guard_objc(item, self.selectedItem) {
	[item reload];
} endguard


- (Opt(NSView *))tableView:(NSTableView *)tableView
	viewForTableColumn:(Opt(NSTableColumn *))tableColumn
	row:(NSInteger)row
{
	return_unless_objc(nil, item,
		self.filteredTableItems[(NSUInteger)row]);
	// item is now a nonnull const local
}


- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
	return_unless_objc(nothing,
		tableView, asOptClass(NSTableView, notification.object)
	);
}


for (id obj in objects) {
	continue_unless_objc(view, asOptClass(NSView, obj));
	[view layoutSubtreeIfNeeded];
}

8. ObjC Casting Helper

Use asOptClass(ClassName, obj) to convert a dynamic Objective-C object to a typed object only when the runtime class check succeeds.

asOptClass(ClassName, obj) returns nil when obj is not of that class.

Prefer it over unchecked casts when dealing with id, notification payloads, collection contents, or other dynamically typed sources.

It combines naturally with guard_objc, return_unless_objc, break_unless_objc, and continue_unless_objc.

Example:

return_unless_objc(nothing,
	tableView, asOptClass(NSTableView, notification.object));

The lower-level castToNonnull_objc(...) helper exists to implement the ObjC guard macros. Do not call it directly in normal application code unless there is a specific low-level macro reason.

9. Weak and Strong Capture Helpers

Prefer the weak/strong helper macros for block captures when they make retain-cycle avoidance and strong rebinding clearer.

Use weak(var) before a block literal when the block should not retain var.

Inside the block, use strong(var) or return_unless_strong(...) before dereferencing the captured object.

The rebound strong local is always named strong_ followed by the original name.

Example:

weak(self);
self.onCloseCallback = ^{
	return_unless_strong(nothing, self);
	strong_self->_settingsController = nil;
};

10. Preference Rule

When standard Objective-C syntax and the enhanced project layer express the same thing equally well, prefer the project layer in this project.

That applies especially to:

As always, consistency and readability still win.