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.
ROmeansnonatomic,readonly.RWmeansnonatomic,readwrite.A_ROmeansatomic,readonly.A_RWmeansatomic,readwrite.
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.
finalmeans__attribute__((objc_subclassing_restricted)).private_methodsmeans__attribute__((objc_direct_members)).internalmeansfinal private_methods.
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:
guard_objc(...)return_unless_objc(returnValue, ...)break_unless_objc(...)continue_unless_objc(...)
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.
weak(var)createsweak_var.strong(var)createsstrong_varfromweak_var.return_unless_strong(returnValue, var)createsstrong_varand returns unless it exists.
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:
RO,RW,A_RO,A_RWfinal,private_methods,internal,direct_callno_default_initsreturn_unless_objc,break_unless_objc,continue_unless_objcasOptClassweak,strong,return_unless_strong
As always, consistency and readability still win.