Skip to content

Instantly share code, notes, and snippets.

@stevebrun
Last active December 7, 2023 17:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevebrun/2c24e11355d32054b67828e93c57aeb2 to your computer and use it in GitHub Desktop.
Save stevebrun/2c24e11355d32054b67828e93c57aeb2 to your computer and use it in GitHub Desktop.
Compile-time constant classes in Objective-C
//
// ConstantClass.m
//
// An example for how to create compile-time constant Objective-C objects that
// can be assigned to any static const variable.
//
// Compile with -fno-objc-arc to allow overriding of retain/release methods.
//
#import <Foundation/Foundation.h>
#import <objc/objc.h>
#import <objc/runtime.h>
// Use pointer authentication if available.
#if defined(__has_include) && __has_include(<ptrauth.h>)
#import <ptrauth.h>
#endif
// Define the pointer authentication attribute as nothing if it doesn't exist.
#ifndef __ptrauth_objc_isa_pointer
#define __ptrauth_objc_isa_pointer
#endif
#pragma mark - Rectangle
@interface Rectangle : NSObject
@property (nonatomic, readonly) NSPoint origin;
@property (nonatomic, readonly) NSSize size;
- (instancetype)initWithOrigin:(NSPoint)origin size:(NSSize)size;
@end
#pragma mark - Constant Rectangle Structure
#if __OBJC2__
/// The _ConstantRectangle class object, stored as a text section symbol.
/// https://stackoverflow.com/questions/24968690/objective-c-mangled-names-objc-class-vs-objc-class-name
extern void const OBJC_CLASS_$__ConstantRectangle;
#elif __OBJC__
/// The _ConstantRectangle class object, stored as an absolute global symbol.
/// https://stackoverflow.com/questions/24968690/objective-c-mangled-names-objc-class-vs-objc-class-name
extern void const OBJC_CLASS_$__ConstantRectangle __asm(".objc_class_name__ConstantRectangle");
#endif
/// The structure we'll use for instances of our _ConstantRectangle class.
struct _ConstantRectangle {
Class __ptrauth_objc_isa_pointer isa;
NSRect rect;
};
#if __OBJC2__
/// A convenience macro for creating our compile-time constant object.
#define ConstRect(x, y, width, height) \
((__bridge Rectangle *)(void *)&(struct _ConstantRectangle){ \
(__bridge Class)&OBJC_CLASS_$__ConstantRectangle, \
{{ x, y }, { width, height }} \
})
#elif __OBJC__
/// https://lists.llvm.org/pipermail/llvm-dev/2007-July/010004.html
#error "Static class references in const expressions are only reliable in the Objective-C 2.0 runtime."
#endif
#pragma mark - Main Program
/// A compile-time constant rectangle object.
static Rectangle *const rect = ConstRect(0, 0, 100, 100);
int main(int argc, char **argv) {
@autoreleasepool {
printf("%s", [[rect description] UTF8String]);
}
return 0;
}
#pragma mark - Rectangle Implementation
@implementation Rectangle
@synthesize origin = _origin;
@synthesize size = _size;
- (instancetype)initWithOrigin:(NSPoint)origin size:(NSSize)size {
if (self = [self init]) {
_origin = origin;
_size = size;
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"[Rectangle origin:%@ size:%@]",
NSStringFromPoint(self.origin), NSStringFromSize(self.size)];
}
- (BOOL)isEqual:(id)object {
if (self == object) { return YES; }
if ([object isKindOfClass:[Rectangle class]]) {
Rectangle *other = object;
return NSEqualPoints(self.origin, other.origin)
&& NSEqualSizes(self.size, other.size);
}
return NO;
}
@end
#pragma mark - Constant Rectangle Implementation
@interface _ConstantRectangle : Rectangle
@end
@implementation _ConstantRectangle
- (NSPoint)origin {
return ((struct _ConstantRectangle *)(__bridge void *)self)->rect.origin;
}
- (NSSize)size {
return ((struct _ConstantRectangle *)(__bridge void *)self)->rect.size;
}
#if defined(__has_feature) && __has_feature(objc_arc)
/// We can't deallocate constant objects.
static void Dealloc(id self, SEL _cmd) {}
/// No use retaining constant objects.
static id Retain(id self, SEL _cmd) { return self; }
/// No use releasing constant objects.
static void Release(id self, SEL _cmd) {}
/// No use autoreleasing constant objects.
static id Autorelease(id self, SEL _cmd) { return self; }
/// No use keeping track of a constant object's retain count.
static NSUInteger RetainCount(id self, SEL _cmd) { return 1; }
/// Override an existing method on the given class.
static BOOL AddMethodOverride(Class self, SEL selector, IMP function) {
Method method = class_getInstanceMethod(self, selector);
char const *types = method_getTypeEncoding(method);
return class_addMethod(self, selector, function, types);
}
/// If ARC is enabled, we can't simply override the existing retain/release
/// methods, so instead we manually swizzle in our overrides when the class
/// is loaded into the runtime.
+ (void)load
{
AddMethodOverride(self, sel_getUid("dealloc"), (IMP)Dealloc);
AddMethodOverride(self, sel_getUid("retain"), (IMP)Retain);
AddMethodOverride(self, sel_getUid("release"), (IMP)Release);
AddMethodOverride(self, sel_getUid("autorelease"), (IMP)Autorelease);
AddMethodOverride(self, sel_getUid("retainCount"), (IMP)RetainCount);
}
#else /* if !defined(__has_feature) || !__has_feature(objc_arc) */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-missing-super-calls"
/// We can't deallocate constant objects.
- (void)dealloc {}
#pragma clang diagnostic pop
/// No use retaining constant objects.
- (instancetype)retain { return self; }
/// No use releasing constant objects.
- (oneway void)release {}
/// No use autoreleasing constant objects.
- (instancetype)autorelease { return self; }
/// No use keeping track of a constant object's retain count.
- (NSUInteger)retainCount { return 1; }
#endif /* !defined(__has_feature) || !__has_feature(objc_arc) */
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment